html5 canvas antialiasing

Lienzo HTML5 y Anti-aliasing



canvas antialiasing (6)

Ahora es 2018, y finalmente tenemos formas baratas de hacer algo al respecto ...

De hecho, dado que la API de segundo contexto ahora tiene una propiedad de filter , y que esta propiedad de filtro puede aceptar SVGFilters , podemos construir un SVGFilter que mantendrá solo píxeles totalmente opacos de nuestros dibujos, y así eliminará el anti-aliasing predeterminado.

Por lo tanto, no desactivará el antialiasing per se, pero proporciona una forma económica tanto en términos de implementación como de rendimiento para eliminar todos los píxeles semitransparentes al dibujar.

No soy realmente un especialista de SVGFilters, por lo que podría haber una mejor manera de hacerlo, pero para el ejemplo, <feComponentTransfer> nodo <feComponentTransfer> para capturar solo píxeles totalmente opacos.

var ctx = canvas.getContext(''2d''); ctx.fillStyle = ''#ABEDBE''; ctx.fillRect(0,0,canvas.width,canvas.height); ctx.fillStyle = ''black''; ctx.font = ''14px sans-serif''; ctx.textAlign = ''center''; // first without filter ctx.fillText(''no filter'', 60, 20); drawArc(); drawTriangle(); // then with filter ctx.setTransform(1, 0, 0, 1, 120, 0); ctx.filter = ''url(#remove-alpha)''; // and do the same ops ctx.fillText(''no alpha'', 60, 20); drawArc(); drawTriangle(); // to remove the filter ctx.filter = ''none''; function drawArc() { ctx.beginPath(); ctx.arc(60, 80, 50, 0, Math.PI * 2); ctx.stroke(); } function drawTriangle() { ctx.beginPath(); ctx.moveTo(60, 150); ctx.lineTo(110, 230); ctx.lineTo(10, 230); ctx.closePath(); ctx.stroke(); } // unrelated // simply to show a zoomed-in version var zCtx = zoomed.getContext(''2d''); zCtx.imageSmoothingEnabled = false; canvas.onmousemove = function drawToZoommed(e) { var x = e.pageX - this.offsetLeft, y = e.pageY - this.offsetTop, w = this.width, h = this.height; zCtx.clearRect(0,0,w,h); zCtx.drawImage(this, x-w/6,y-h/6,w, h, 0,0,w*3, h*3); }

<svg width="0" height="0" style="position:absolute;z-index:-1;"> <defs> <filter id="remove-alpha" x="0" y="0" width="100%" height="100%"> <feComponentTransfer> <feFuncA type="discrete" tableValues="0 1"></feFuncA> </feComponentTransfer> </filter> </defs> </svg> <canvas id="canvas" width="250" height="250" ></canvas> <canvas id="zoomed" width="250" height="250" ></canvas>

Y para los que no les gusta agregar un elemento <svg> en su DOM, también puede guardarlo como un archivo svg externo y establecer la propiedad del filter en path/to/svg_file.svg#remove-alpha .

Cómo activar el suavizado en un lienzo .

El siguiente código no dibuja una línea suave:

var context = mainCanv.getContext("2d"); if (context) { context.moveTo(0,0); context.lineTo(100,75); context.strokeStyle = "#df4b26"; context.lineWidth = 3; context.stroke(); }



Aquí hay una solución alternativa que requiere que dibuje líneas píxel por píxel, pero evitará el anti aliasing.

// some helper functions // finds the distance between points function DBP(x1,y1,x2,y2) { return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); } // finds the angle of (x,y) on a plane from the origin function getAngle(x,y) { return Math.atan(y/(x==0?0.01:x))+(x<0?Math.PI:0); } // the function function drawLineNoAliasing(ctx, sx, sy, tx, ty) { var dist = DBP(sx,sy,tx,ty); // length of line var ang = getAngle(tx-sx,ty-sy); // angle of line for(var i=0;i<dist;i++) { // for each point along the line ctx.fillRect(Math.round(sx + Math.cos(ang)*i), // round for perfect pixels Math.round(sy + Math.sin(ang)*i), // thus no aliasing 1,1); // fill in one pixel, 1x1 } }

Básicamente, usted encuentra la longitud de la línea y recorre paso por paso esa línea, redondeando cada posición y completando un píxel.

Llámalo con

var context = cv.getContext("2d"); drawLineNoAliasing(context, 20,30,20,50); // line from (20,30) to (20,50)


No he necesitado encender el anti-alias porque está activado por defecto pero he tenido que apagarlo. Y si se puede apagar, también se puede encender.

ctx.imageSmoothingEnabled = true;

Normalmente lo cierro cuando estoy trabajando en mi RPG de lienzo, así que cuando hago zoom las imágenes no se ven borrosas.


Puede traducir lienzo por distancia de medio píxel.

ctx.translate(0.5, 0.5);

Inicialmente el punto de posicionamiento del lienzo entre los píxeles físicos.


Si necesita control de nivel de píxel sobre lienzo, puede hacerlo utilizando createImageData y putImageData .

HTML:

<canvas id="qrCode" width="200", height="200"> QR Code </canvas>

Y JavaScript:

function setPixel(imageData, pixelData) { var index = (pixelData.x + pixelData.y * imageData.width) * 4; imageData.data[index+0] = pixelData.r; imageData.data[index+1] = pixelData.g; imageData.data[index+2] = pixelData.b; imageData.data[index+3] = pixelData.a; } element = document.getElementById("qrCode"); c = element.getContext("2d"); pixcelSize = 4; width = element.width; height = element.height; imageData = c.createImageData(width, height); for (i = 0; i < 1000; i++) { x = Math.random() * width / pixcelSize | 0; // |0 to Int32 y = Math.random() * height / pixcelSize| 0; for(j=0;j < pixcelSize; j++){ for(k=0;k < pixcelSize; k++){ setPixel( imageData, { x: x * pixcelSize + j, y: y * pixcelSize + k, r: 0 | 0, g: 0 | 0, b: 0 * 256 | 0, a: 255 // 255 opaque }); } } } c.putImageData(imageData, 0, 0);

Muestra de trabajo aquí