summaryrefslogtreecommitdiff
path: root/halftoneqr.js
diff options
context:
space:
mode:
Diffstat (limited to 'halftoneqr.js')
-rw-r--r--halftoneqr.js245
1 files changed, 245 insertions, 0 deletions
diff --git a/halftoneqr.js b/halftoneqr.js
new file mode 100644
index 0000000..b68d789
--- /dev/null
+++ b/halftoneqr.js
@@ -0,0 +1,245 @@
+// original by http://lach.la
+
+var pixelSize = 2;
+var blockSize = (3*pixelSize);
+var image;
+var has_image = false;
+var has_canvas = false;
+var qr, controls
+
+function halftoneQR(QRBytes, controlBytes, image) {
+
+ var canvas = $('#output').get(0);
+ var ctx = canvas.getContext('2d');
+
+ drawPixel();
+
+ ctx.fillStyle = '#fff'
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ var canvasThreshold = $('#imageThreshold').get(0);
+ var ctxThreshold = canvasThreshold.getContext('2d');
+
+ 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 === '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 byteRow = 0; byteRow < controlBytes.length; byteRow++) {
+ for (var byteCell = 0; byteCell < controlBytes[byteRow].length; byteCell++) {
+ if (controlBytes[byteRow][byteCell] !== null) {
+ if (controlBytes[byteRow][byteCell] === true) {
+ ctx.fillStyle = 'black';
+ } else {
+ ctx.fillStyle = 'white';
+ }
+ ctx.fillRect(byteRow * blockSize, byteCell * blockSize, blockSize, blockSize);
+ }
+ };
+ };
+
+ $('#download').attr('href', $('#output').get(0).toDataURL());
+
+}
+
+function drawImage() {
+ var canvasColour = $('#imageColour').get(0);
+ var ctxColour = canvasColour.getContext('2d');
+
+ ctxColour.clearRect(0,0,canvasColour.width, canvasColour.height);
+ ctxColour.drawImage(image, 0, 0, canvasColour.width, canvasColour.height);
+ console.log('hi')
+ drawPixel();
+}
+
+function drawPixel() {
+ var canvasColour = $('#imageColour').get(0);
+ var canvasPixel = $('#imagePixel').get(0);
+ var ctxPixel = canvasPixel.getContext('2d');
+ var canvasTemp = document.createElement('canvas');
+ canvasTemp.width = canvasTemp.height = (canvasPixel.width / pixelSize);
+ var ctxTemp = canvasTemp.getContext('2d');
+
+ ctxPixel.imageSmoothingEnabled =
+ ctxPixel.mozImageSmoothingEnabled =
+ ctxPixel.msImageSmoothingEnabled =
+ ctxPixel.webkitImageSmoothingEnabled =
+ ctxTemp.imageSmoothingEnabled =
+ ctxTemp.mozImageSmoothingEnabled =
+ ctxTemp.msImageSmoothingEnabled =
+ ctxTemp.webkitImageSmoothingEnabled = false;
+
+ ctxTemp.drawImage(canvasColour, 0, 0, canvasTemp.width, canvasTemp.height);
+ ctxPixel.drawImage(canvasTemp, 0, 0, canvasPixel.width, canvasPixel.height);
+
+ drawThreshold();
+}
+
+function drawThreshold() {
+ var canvasPixel = $('#imagePixel').get(0);
+ var ctxPixel = canvasPixel.getContext('2d');
+ var canvasThreshold = $('#imageThreshold').get(0);
+ var ctxThreshold = canvasThreshold.getContext('2d');
+
+ var pixels = ctxPixel.getImageData(0,0,canvasPixel.width,canvasPixel.height);
+ var d = pixels.data;
+ var width = Math.sqrt(d.length / 4) / pixelSize;
+ for (var i=0; i<d.length; i+=4) {
+ var r = d[i];
+ var g = d[i+1];
+ var b = d[i+2];
+ var grey = (r * 0.2126 + g * 0.7152 + b * 0.0722);
+ //var v = (grey >= 127) ? 255 : 0;
+ //d[i] = d[i+1] = d[i+2] = v;
+ d[i] = d[i+1] = d[i+2] = grey;
+ }
+
+ for (var i=0; i<d.length; i+=4) {
+ var grey = d[i];
+ var v = (grey >= 127) ? 255 : 0;
+
+ // Dithering
+ var error = (grey - v) / 8;
+ var i2 = i / 4;
+ var row = Math.floor(i2 / width);
+ var cell = i2 % width;
+
+ d[i] = d[i+1] = d[i+2] = v;
+
+ d[(((row + 0) * width) + (cell + 1)) * 4] = d[(((row + 0) * width) + (cell + 1)) * 4 + 1] = d[(((row + 0) * width) + (cell + 1)) * 4 + 2] = d[(((row + 0) * width) + (cell + 1)) * 4] + error;
+ d[(((row + 0) * width) + (cell + 2)) * 4] = d[(((row + 0) * width) + (cell + 2)) * 4 + 1] = d[(((row + 0) * width) + (cell + 2)) * 4 + 2] = d[(((row + 0) * width) + (cell + 2)) * 4] + error;
+ d[(((row + 1) * width) + (cell - 1)) * 4] = d[(((row + 1) * width) + (cell - 1)) * 4 + 1] = d[(((row + 1) * width) + (cell - 1)) * 4 + 2] = d[(((row + 1) * width) + (cell - 1)) * 4] + error;
+ d[(((row + 1) * width) + (cell + 0)) * 4] = d[(((row + 1) * width) + (cell + 0)) * 4 + 1] = d[(((row + 1) * width) + (cell + 0)) * 4 + 2] = d[(((row + 1) * width) + (cell + 0)) * 4] + error;
+ d[(((row + 1) * width) + (cell + 1)) * 4] = d[(((row + 1) * width) + (cell + 1)) * 4 + 1] = d[(((row + 1) * width) + (cell + 1)) * 4 + 2] = d[(((row + 1) * width) + (cell + 1)) * 4] + error;
+ d[(((row + 2) * width) + (cell + 0)) * 4] = d[(((row + 2) * width) + (cell + 0)) * 4 + 1] = d[(((row + 2) * width) + (cell + 0)) * 4 + 2] = d[(((row + 2) * width) + (cell + 0)) * 4] + error;
+ }
+ ctxThreshold.putImageData(pixels, 0, 0);
+}
+
+function generate() {
+ var text = $('#input').val() || "http://asdf.us/";
+
+ var errorLevel = 'H';
+
+ var sizes = {
+ L: [152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 1240],
+ M: [128, 224, 352, 512, 688, 864, 992, 700, 700, 524],
+ Q: [104, 176, 272, 384, 286, 608, 508, 376, 608, 434],
+ H: [72, 128, 208, 288, 214, 480, 164, 296, 464, 346]
+ };
+
+ var userSize = parseInt($('#size').val());
+ var QRsize = -1;
+ if (userSize === 0) {
+ for (var i = 0; i < sizes[errorLevel].length; i++) {
+ if (text.length < sizes[errorLevel][i]) {
+ QRsize = i + 1;
+ break;
+ }
+ };
+ } else {
+ if (text.length < sizes[errorLevel][userSize - 1]) {
+ QRsize = userSize;
+ }
+ }
+ if (QRsize == -1) {
+ if (userSize === 0) {
+ if (errorLevel === 'H') {
+ alert('Too much text.');
+ } else {
+ alert('Too much text. Try decreasing the error level.');
+ }
+ } else {
+ alert('Too much text. Try decreasing the error level or increasing the size.');
+ }
+ return;
+ }
+
+ qr = qrcode(QRsize, errorLevel);
+ qr.addData(text);
+ qr.make();
+
+ controls = qrcode(QRsize, errorLevel);
+ controls.addData(text);
+ controls.make(true);
+}
+function render(){
+ halftoneQR(qr.returnByteArray(), controls.returnByteArray());
+}
+$("#file_upload").on('change', function(e){
+ e.preventDefault();
+
+ var files = e.originalEvent.dataTransfer ? e.originalEvent.dataTransfer.files : e.originalEvent.target.files;
+ var file = files[0];
+ var reader = new FileReader();
+
+ reader.onload = function(event) {
+ //event.target.result;
+ var imageColour = new Image();
+ imageColour.onload = function() {
+ has_image = true;
+ image = this;
+ drawImage()
+ render()
+ }
+ imageColour.src = event.target.result;
+ };
+ reader.readAsDataURL(file);
+
+ return false;
+})
+$("#input").on('input', function(){
+ generate()
+ if ($("#size").val() === 'auto') {
+ resize()
+ }
+ render()
+})
+$("#error_level").on('change', function(){
+ generate()
+ render()
+})
+$("#size").on('change', function(){
+ generate()
+ resize()
+ render()
+})
+function resize(){
+ var canvas = $('#output').get(0);
+ canvas.width = canvas.height = qr.returnByteArray().length * (3*pixelSize);
+ var ctx = canvas.getContext('2d');
+
+ $('#imageColour, #imageThreshold, #imagePixel').attr({
+ width: canvas.width,
+ height: canvas.height
+ })
+}
+$(function() {
+ generate()
+ resize()
+ render()
+})
+