web-dev-qa-db-fra.com

HTML5 Canvas et anti-aliasing

Comment activer le anti-aliasing sur un canvas .

Le code suivant ne dessine pas une ligne lisse:

var context = mainCanv.getContext("2d");
if (context) {
   context.moveTo(0,0);
   context.lineTo(100,75);

   context.strokeStyle = "#df4b26";
   context.lineWidth = 3;
   context.stroke();
}
52
KRouane

L'anti-aliasing ne peut être activé ou désactivé et est contrôlé par le navigateur.

Puis-je désactiver l'anticrénelage sur un élément HTML <canvas>?

24
Gaurav

Vous pouvez traduire un canevas par demi-pixel.

ctx.translate(0.5, 0.5);

Initialement, le point de positionnement de la toile entre les pixels physiques.

37
Bacher

Je n'ai pas eu besoin d'activer l'anti-alias car il est activé par défaut, mais j'ai dû le désactiver. Et s'il peut être désactivé, il peut également l'être. 

ctx.imageSmoothingEnabled = true;

D'habitude, je l'éteins lorsque je travaille sur mon cadre de travail sur toile. Ainsi, lorsque je fais un zoom avant, les images ne sont pas floues. 

19
zachdyer

Nous sommes maintenant en 2018 et nous avons enfin des moyens peu coûteux de faire quelque chose pour y remédier ...

En effet, puisque l’API de contexte 2d a maintenant une propriété filter , et que cette propriété de filtre peut accepter SVGFilters , nous pouvons construire un SVGFilter qui ne conservera que les pixels totalement opaques de nos dessins, éliminant ainsi les anti-aliasing par défaut.

Ainsi, il ne désactivera pas l'antialiasing en soi, mais fournira un moyen peu coûteux, tant en termes de mise en œuvre que de performances, de supprimer tous les pixels semi-transparents lors du dessin.

Je ne suis pas vraiment un spécialiste des filtres SVG, il y aurait peut-être une meilleure façon de le faire, mais pour l'exemple, j'utiliserai un nœud <feComponentTransfer> pour ne capturer que les pixels complètement opaques.

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>

Et pour ceux qui n'aiment pas ajouter un élément <svg> dans leur DOM, vous pouvez également l'enregistrer en tant que fichier svg externe et définir la propriété filter sur path/to/svg_file.svg#remove-alpha.

8
Kaiido

Voici une solution de contournement qui nécessite de tracer des lignes pixel par pixel, tout en évitant l’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
    }
}

En gros, vous trouvez la longueur de la ligne et vous la parcourez pas à pas en arrondissant chaque position et en remplissant un pixel.

Appelez-le avec

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

Si vous avez besoin d'un contrôle au niveau des pixels sur le canevas, vous pouvez utiliser createImageData et putImageData .

HTML:

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

Et 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);

Exemple de travail ici

2
Jordan

je suppose donc que c'est un peu hors d'usage maintenant, mais une façon de le faire est d'utiliser document.body.style.zoom=2.0;, mais si vous faites cela, toutes les mesures de la toile devront être divisées par le zoom. Également, augmentez le zoom pour augmenter le crénelage. Ceci est utile car il est réglable. Aussi, si vous utilisez cette méthode, je vous suggère de faire en sorte que les fonctions fassent comme ctx.fillRect() etc. mais en prenant en compte le zoom. Par exemple.
function fillRect(x,y,width,height){ var zoom = document.body.style.zoom; ctx.fillRect(x/zoom,y/zoom,width/zoom,height/zoom); }
J'espère que cela t'aides!

Sidenote: cela peut également être utilisé pour accentuer les bords des cercles afin qu’ils ne soient pas aussi flous. Utilisez simplement un zoom tel que 0.5!

0
TOGD