function drawQRToCanvasFancy(qr, outCanvas, scale, options) { if (typeof options === 'undefined') { options = { fgColor: '#000', bgColor: 'false', /*bgImage: 'false',*/ roundModules: false, connectRoundModules: false, roundInsideCorners: false, roundDiagonalCorners: false, blankDots: false, blankDotsScale: 0, blankDotsColor: '' }; } /* create the temp canvas if it doesn't already exist */ if (typeof tempCanvas === 'undefined') { tempCanvas = document.createElement('canvas'); tempCanvasContext = tempCanvas.getContext('2d'); } var canvas = tempCanvas; var context = tempCanvasContext; var canvasSize = ((qr.dim + 8) * scale); canvas.setAttribute('width', canvasSize.toString() + 'px'); canvas.setAttribute('height', canvasSize.toString() + 'px'); if (options.bgColor !== false) { context.fillStyle = options.bgColor; context.fillRect(0, 0, canvasSize, canvasSize); } context.translate(4 * scale, 4 * scale); for (var i = 0; i < qr.symbol.length; i++) { var x = i % qr.dim; var y = (i - x) / qr.dim; if (qr.symbol[i]) { if (options.roundModules) { /* round modules */ /* squared corners on round modules. this part is pretty thick, but it speeds up rendering ~300% over the previous version */ if (options.squareCardinalConnections || options.squareDiagonalConnections) { /* draw four corners */ context.beginPath(); for (var j = 0; j < 4; j++) { /* these offsets progress counter-clockwise, starting in the bottom right -- this makes it easier to base the angles off of j */ var oy = (j&2) ? -1 : 1; var ox = ((j&1) ? -1 : 1) * oy; if ((options.squareCardinalConnections && (qr.getBit(x, y+oy) || qr.getBit(x+ox, y))) || (options.squareDiagonalConnections && qr.getBit(x+ox, y+oy))) { context.lineTo(x*scale + (ox==1?scale:0), y*scale + (oy==1?scale:0)); } else { /* not a square corner, draw it round */ context.arc((x+0.5)*scale, (y+0.5)*scale, 0.5*scale, j*Math.PI*0.5, (j+1)*Math.PI*0.5); } } context.closePath(); eraseAndFill(context, options.fgColor); } else { /* plain round modules, just draw a circle */ context.beginPath(); context.arc((x+0.5)*scale, (y+0.5)*scale, 0.5*scale, 0, Math.PI*2); eraseAndFill(context, options.fgColor); } } else { /* square modules, just draw a rectangle */ context.beginPath(); context.rect(x*scale, y*scale, scale, scale); eraseAndFill(context, options.fgColor); } } else { /* option roundInsideCorners causes the corners of white modules between two diagonal black modules to be rounded */ if (options.roundInsideCorners) { context.fillStyle = options.fgColor; for (var j = 0; j < 2; j++) { for (var k = 0; k < 2; k++) { var ox = [-1,1][j]; var oy = [-1,1][k]; /* check to see if this is a corner, then draw a corner path */ if (qr.getBit(x+ox, y) && qr.getBit(x, y+oy) && qr.getBit(x+ox,y+oy)) { context.beginPath(); var v = k ? (j ? 0 : Math.PI*0.5) : (j ? Math.PI*1.5 : Math.PI); context.arc((x+0.5)*scale, (y+0.5)*scale, 0.5*scale, v, v + Math.PI*0.5); context.lineTo((x+j)*scale, (y+k)*scale); context.closePath(); eraseAndFill(context, options.fgColor); } } } } if (options.roundDiagonalCorners) { context.fillStyle = options.fgColor; for (var j = 0; j < 2; j++) { for (var k = 0; k < 2; k++) { var ox = [-1,1][j]; var oy = [-1,1][k]; /* check to see if this is a corner, then draw a corner path */ if (qr.getBit(x+ox, y) && qr.getBit(x, y+oy) && !qr.getBit(x+ox,y+oy)) { context.beginPath(); var v = k ? (j ? 0 : Math.PI*0.5) : (j ? Math.PI*1.5 : Math.PI); context.arc((x+0.5)*scale, (y+0.5)*scale, 0.5*scale, v, v + Math.PI*0.5); context.lineTo((x+j)*scale, (y+k)*scale); context.closePath(); eraseAndFill(context, options.fgColor); } } } } if (options.blankDots) { context.beginPath(); context.arc((x+0.5) * scale, (y+0.5)*scale, options.blankDotsScale*scale, 0, Math.PI*2); eraseAndFill(context, options.blankDotsColor); } } } outCanvas.setAttribute('width', canvasSize.toString() + 'px'); outCanvas.setAttribute('height', canvasSize.toString() + 'px'); var outContext = outCanvas.getContext('2d'); outContext.drawImage(canvas, 0, 0); } function eraseAndFill(ctx, col) { var oldcomp = ctx.globalCompositeOperation; ctx.fillStyle = '#fff'; ctx.globalCompositeOperation = 'destination-out'; ctx.fill(); ctx.fillStyle = col; ctx.globalCompositeOperation = oldcomp; ctx.fill(); }