Halftone QR Code Generator

HTML

<!-- Licence: MIT -->
<h1>Halftone QR Code Generator</h1>
<h2>Drop an image into your browser, then click generate.</h2>
QR content:<br />
<textarea id="input" cols="50" rows="5">Kitty!</textarea><br />
Redundancy (error correction level):
<select id="error_level">
    <option value="L">Low (7%)</option>
    <option value="M">Medium (15%)</option>
    <option value="Q">Quartile (25%)</option>
    <option value="H" selected>High (30%)</option>
</select><br />
QR size:
<select id="size">
    <option value="0">Auto</option>
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6" selected>6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
</select> (6 gives the largest blank square area)<br />
Background Type:
<select id="background">
    <option value="image" selected>Image</option>
    <option value="noise">Noise</option>
    <option value="trans">Transparent</option>
</select><br />
<input type="button" id="go" value="Generate" /><br />

<canvas id="imageColour"></canvas>
<canvas id="imagePixel"></canvas>
<canvas id="imageThreshold"></canvas>
<a id="download" href="about:blank" download="qr.png" target="_blank" title="Download QR Code"><canvas id="output"></canvas></a><br />
<a href="http://lach.la">By Lachlan</a>. Licence: <a href="http://www.opensource.org/licenses/mit-license.php">MIT</a>
<br /><br />
Cat photo by Cayambe [<a href="http://creativecommons.org/licenses/by-sa/3.0">CC-BY-SA-3.0</a> or <a href="http://www.gnu.org/copyleft/fdl.html">GFDL</a>], <a href="http://commons.wikimedia.org/wiki/File%3ACat_close-up_2004_b.jpg">via Wikimedia Commons</a>

<div id="overlay">Drop image</div>

CSS

html, body {
    height: 100%;
    margin: 0;
}

body {
    position: relative;
}

h1, h2 {
    margin: 0;
}

#overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.5);
    z-index: 10;
    font-size: 100px;
    color: white;
    text-align: center;
}

body.hover #overlay {
    display: block;
}

canvas {
    margin: 5px;
    box-shadow: 0 0 5px black;
}

/* Hidden for now */
#imagePixel {
    display: none;
}

#output {
    background-color: grey;
}
</style>
<script>
//---------------------------------------------------------------------
//
// QR Code Generator for JavaScript
//
// Copyright (c) 2009 Kazuhiko Arase
//
// URL: http://www.d-project.com/
//
// Licensed under the MIT license:
//	http://www.opensource.org/licenses/mit-license.php
//
// The word 'QR Code' is registered trademark of
// DENSO WAVE INCORPORATED
//	http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//---------------------------------------------------------------------

var qrcode = function() {

	//---------------------------------------------------------------------
	// qrcode
	//---------------------------------------------------------------------

	/**
	 * qrcode
	 * @param typeNumber 1 to 10
	 * @param errorCorrectLevel 'L','M','Q','H'
	 */
	var qrcode = function(typeNumber, errorCorrectLevel) {

		var PAD0 = 0xEC;
		var PAD1 = 0x11;

		var _typeNumber = typeNumber;
		var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel];
		var _modules = null;
		var _moduleCount = 0;
		var _dataCache = null;
		var _dataList = new Array();

		var _this = {};

		var makeImpl = function(test, maskPattern, onlyControl) {
			
			onlyControl = onlyControl || false;
			_moduleCount = _typeNumber * 4 + 17;
			_modules = function(moduleCount) {
				var modules = new Array(moduleCount);
				for (var row = 0; row < moduleCount; row += 1) {
					modules[row] = new Array(moduleCount);
					for (var col = 0; col...

JavaScript

var pixelSize = 2;
var blockSize = (3*pixelSize);
var image;
var has_image = false;

function halftoneQR(QRBytes, controlBytes, image) {
    
    var canvas = $('#output').get(0);
    canvas.width = canvas.height = QRBytes.length * (3*pixelSize);
    var ctx = canvas.getContext('2d');
    var background = $('#background').val();
    
    $('#imageColour, #imageThreshold, #imagePixel').attr({
        width: canvas.width,
        height: canvas.height
    });
    if (has_image) {
        // Re-draw image (incase size changed)
        drawImage();
    }
    
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    var canvasThreshold = $('#imageThreshold').get(0);
    var ctxThreshold = canvasThreshold.getContext('2d');
    
    if (has_image && background === 'image') {
        ctx.drawImage(canvasThreshold, 0, 0, canvas.width, canvas.height);
    }
    
    for (var byteRow = 0; byteRow < QRBytes.length; byteRow++) {
        for (var byteCell = 0; byteCell < QRBytes[byteRow].length; byteCell++) {
            
            if ((background === 'image' && !has_image) || background === 'noise') {
                // Draw random bytes
                ctx.fillStyle = 'black';
                for (var subRow = 0; subRow < 3; subRow++) {
                    for (var subCell = 0; subCell < 3; subCell++) {
                        ctx.fillStyle = 'black';
                        if (Math.random() < 0.5) {
                            ctx.fillStyle = 'white';
                        }
                        ctx.fillRect(byteRow * blockSize + (subRow * pixelSize), byteCell * blockSize + (subCell * pixelSize), pixelSize, pixelSize);
                    }
                }
            }
            
            // Middle Cell
            ctx.fillStyle = QRBytes[byteRow][byteCell] ? 'black' : 'white';
            ctx.fillRect(byteRow * blockSize + pixelSize, byteCell * blockSize + pixelSize, pixelSize, pixelSize);
        }
    }
    
    // Re-draw control bytes
    for (var...