JavaScript per character: DrawImage vs FillText

JavaScript performance comparison

Test case created by Ross Brunton

Preparation code

<canvas id="testarea" width="3500" height="20"></canvas>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script>
createCanvas = function(width, height) {
	var hold = document.createElement("canvas");
	hold.width = width;
	hold.height = height;
	hold.style.imageRendering = "-webkit-optimize-contrast";
	hold.getContext("2d").textBaseline = "middle";
	return hold;
};

var longStr = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eros orci, accumsan at tempus tincidunt, faucibus sit amet erat. Etiam tempor cursus leo. Nullam non eros eu quam fermentum pharetra. Vivamus nunc purus, sollicitudin mollis vulputate non, mollis quis ante. Aliquam vitae eros eget lorem fermentum blandit. Integer dictum suscipit massa, eget viverra nisl venenatis sed. Quisque vehicula eleifend risus, vitae facilisis lacus vehicula quis. Maecenas quam elit, molestie id feugiat non, sodales mollis enim. Nullam consequat rhoncus hendrerit. Nulla facilisi. Nulla est erat, congue id lobortis at, pharetra vel risus. Donec sit amet magna lorem, semper tincidunt ligula. Mauris tempus ultricies orci sed sagittis.";

var table = {"segments":[], "segmentFree":new Uint8Array(50)};
var target = $("#testarea")[0];
target.getContext("2d").font = "14px sans";

addToTable = function(char) {
	if(char in table) return false;
	
	if(!table.segments[0]) {
		table.segments[0] = createCanvas(255, 14).getContext("2d");
		table.segments[0].font = "14px sans";
	}
	
	var width = table.segments[0].measureText(char).width;
	var seg = -1;
	
	for(var i = 0; i < table.segmentFree.length; i ++) {
		if(width + table.segmentFree[i] + 3 < 255) {
			seg = i;
			break;
		}
	}
	
	if(seg == -1) {
		console.warn("Out of space for character "+char+"!");
		return true;
	}
	
	if(!table.segments[seg]) {
		table.segments[seg] = createCanvas(255, 14).getContext("2d");
		table.segments[seg].font = "14px sans";
	}
	
	table[char] = new Uint8Array(3);
	table[char][0] = seg;
	table[char][1] = table.segmentFree[seg];
	table[char][2] = width;
	
	table.segmentFree[seg] += width + 3;
	
	table.segments[seg].fillText(char, table[char][1], this.padding + (this.size>>1));
};

for(var i = 0; i < "Hello World!".length; i ++) {
	addToTable("Hello World!"[i]);
}

for(var i = 0; i < longStr.length; i ++) {
	addToTable(longStr[i]);
}

function imageTextA(text) {
	var cursor = 0;
	while(text !== "") {
		var char = text[0];

		target.getContext("2d").drawImage(table.segments[table[char][0]].canvas,
			table[char][1], 0, table[char][2], 14,
			cursor, 0, table[char][2], 14
		);
		cursor += table[char][2];

		text = text.substr(1);
	}
}

function imageTextB(text) {
	var cursor = 0;
	for(var i = 0; i < text.length; i ++) {
		var char = text[i];

		target.getContext("2d").drawImage(table.segments[table[char][0]].canvas,
			table[char][1], 0, table[char][2], 14,
			cursor, 0, table[char][2], 14
		);
		cursor += table[char][2];
	}
}

function fillTextLoop(text) {
	var cursor = 0;
	for(var i = 0; i < text.length; i ++) {
		var char = text[i];

		target.getContext("2d").fillText(char, cursor, 0);
		cursor += table[char][2];
	}
}

function fillText(text) {
	target.getContext("2d").fillText(text, 0, 10);
}
</script>
      
<script>
Benchmark.prototype.setup = function() {
  target.getContext("2d").clearRect(0, 0, 3500, 20);

};
</script>

Preparation code output

<canvas id="testarea" width="3500" height="20"></canvas> <script> createCanvas = function(width, height) { var hold = document.createElement("canvas"); hold.width = width; hold.height = height; hold.style.imageRendering = "-webkit-optimize-contrast"; hold.getContext("2d").textBaseline = "middle"; return hold; }; var longStr = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc eros orci, accumsan at tempus tincidunt, faucibus sit amet erat. Etiam tempor cursus leo. Nullam non eros eu quam fermentum pharetra. Vivamus nunc purus, sollicitudin mollis vulputate non, mollis quis ante. Aliquam vitae eros eget lorem fermentum blandit. Integer dictum suscipit massa, eget viverra nisl venenatis sed. Quisque vehicula eleifend risus, vitae facilisis lacus vehicula quis. Maecenas quam elit, molestie id feugiat non, sodales mollis enim. Nullam consequat rhoncus hendrerit. Nulla facilisi. Nulla est erat, congue id lobortis at, pharetra vel risus. Donec sit amet magna lorem, semper tincidunt ligula. Mauris tempus ultricies orci sed sagittis."; var table = {"segments":[], "segmentFree":new Uint8Array(50)}; var target = $("#testarea")[0]; target.getContext("2d").font = "14px sans"; addToTable = function(char) { if(char in table) return false; if(!table.segments[0]) { table.segments[0] = createCanvas(255, 14).getContext("2d"); table.segments[0].font = "14px sans"; } var width = table.segments[0].measureText(char).width; var seg = -1; for(var i = 0; i < table.segmentFree.length; i ++) { if(width + table.segmentFree[i] + 3 < 255) { seg = i; break; } } if(seg == -1) { console.warn("Out of space for character "+char+"!"); return true; } if(!table.segments[seg]) { table.segments[seg] = createCanvas(255, 14).getContext("2d"); table.segments[seg].font = "14px sans"; } table[char] = new Uint8Array(3); table[char][0] = seg; table[char][1] = table.segmentFree[seg]; table[char][2] = width; table.segmentFree[seg] += width + 3; table.segments[seg].fillText(char, table[char][1], this.padding + (this.size>>1)); }; for(var i = 0; i < "Hello World!".length; i ++) { addToTable("Hello World!"[i]); } for(var i = 0; i < longStr.length; i ++) { addToTable(longStr[i]); } function imageTextA(text) { var cursor = 0; while(text !== "") { var char = text[0]; target.getContext("2d").drawImage(table.segments[table[char][0]].canvas, table[char][1], 0, table[char][2], 14, cursor, 0, table[char][2], 14 ); cursor += table[char][2]; text = text.substr(1); } } function imageTextB(text) { var cursor = 0; for(var i = 0; i < text.length; i ++) { var char = text[i]; target.getContext("2d").drawImage(table.segments[table[char][0]].canvas, table[char][1], 0, table[char][2], 14, cursor, 0, table[char][2], 14 ); cursor += table[char][2]; } } function fillTextLoop(text) { var cursor = 0; for(var i = 0; i < text.length; i ++) { var char = text[i]; target.getContext("2d").fillText(char, cursor, 0); cursor += table[char][2]; } } function fillText(text) { target.getContext("2d").fillText(text, 0, 10); } </script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Short: FillText
fillText("Hello World!");
pending…
Short: With Substr
imageTextA("Hello World!");
pending…
Short: With For Loop
imageTextB("Hello World!");
pending…
Short: FillText Loop
fillTextLoop("Hello World");
pending…
Long: FillText
fillText(longStr);
pending…
Long: With Substr
imageTextA(longStr);
pending…
Long: With For Loop
imageTextB(longStr);
pending…
Long: FillText Loop
fillTextLoop(longStr);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments