Html5 Canvas and Anti-Aliasing

HTML5 Canvas and Anti-aliasing

Anti-aliasing cannot be turned on or off, and is controlled by the browser.

Can I turn off antialiasing on an HTML <canvas> element?

How to avoid antialiasing effect in HTML5 Canvas

You can add support for line-width relative to zoom (just make sure restore() is applied after stroke or all settings will return before anything is drawn):

var lineWidth = 1;                       // line width

function draw() {
context.save();
context.beginPath();
context.scale(zoom, zoom);
context.lineWidth = zoom * lineWidth; // line-width * zoom
context.moveTo(100, 50);
context.lineTo(100, 100);
context.stroke();
context.restore(); // restore last
}

Modified fiddle

If you want to keep about 1 pixel regardless of scale you can invert the line-width formula:

  context.lineWidth = 1 / (zoom * lineWidth);

Result

However, there will be small rounding errors affecting the anti-aliasing processing on some scales.

Solution

The only real way to avoid this issue is to manually apply a matrix to points representing the line, make the values integers, then render the result of those as a line using the Bresenham or, IMO, the better and faster EFLA algorithm and via ImageData, pixel-by-pixel, and finally push that to the bitmap. You can wrap all this into a single function of course.

This also mean you need to track the matrix. In newer browsers you can use currentTransform and soon getTransform() to obtain current transformation, or you can use a custom matrix solution for cross-browser and backward compatibility (there are many out there, here is mine).

There is currently no way to turn off anti-aliasing for vectors rasterized to the canvas.

Demo

I didn't implement boundary checks in this quick demo, but that is something you need to implement as well so the lines doesn't wrap around (ie. only draw if x >= 0 && x < width etc. .. per pixel).

window.onload = function() {  var canvas = document.getElementById('c');var context = canvas.getContext('2d');var matrix = new Matrix();var zoom = 1.0;
function clear() { context.clearRect(0, 0, canvas.width, canvas.height);}
function draw() { matrix.reset(); // replaces save/restore matrix.scale(zoom, zoom);
// manually draw line via matrix and EFLA line(context, 100, 50, 100, 100);}
// custom line functionfunction line(context, x1, y1, x2, y2) {
// instead of transforming context, apply matrix to points: var p1 = matrix.applyToPoint(x1, y1); var p2 = matrix.applyToPoint(x2, y2);
// create a bitmap (for demo), or obtain an existing one (getImageData) var idata = context.createImageData(canvas.width, canvas.height); var data32 = new Uint32Array(idata.data.buffer); _line(data32, p1.x|0, p1.y|0, p2.x|0, p2.y|0, canvas.width); context.putImageData(idata, 0, 0);}
// EFLA line algorithmfunction _line(data, x1, y1, x2, y2, w) {
var dlt, mul, sl = y2 - y1, ll = x2 - x1, yl = false, lls = ll >> 31, sls = sl >> 31, i;
if ((sl ^ sls) - sls > (ll ^ lls) - lls) { sl ^= ll; ll ^= sl; sl ^= ll; yl = true }
dlt = ll < 0 ? -1 : 1; mul = (ll === 0) ? sl : sl / ll;
if (yl) { x1 += 0.5; for (i = 0; i !== ll; i += dlt) setPixel(data, (x1 + i * mul)|0, y1 + i, w) } else { y1 += 0.5; for (i = 0; i !== ll; i += dlt) setPixel(data, x1 + i, (y1 + i * mul)|0, w) }}
// Set a pixel (black for demo)function setPixel(data, x, y, w) {data[y * w + x] = 0xff000000}
$('#c').mousewheel(function(e) { e.preventDefault(); if (e.originalEvent.deltaY < 0) { zoom *= 1.1; } else { zoom /= 1.1; } clear(); draw();});

draw(); };
body {overflow:hidden}
<script src="//code.jquery.com/jquery-3.1.1.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js"></script><script src="//cdn.rawgit.com/epistemex/transformation-matrix-js/master/matrix.min.js"></script>
<canvas id="c" width="800" height="600"></canvas>

Html5 canvas drawImage: how to apply antialiasing

Cause

Some images are just very hard to down-sample and interpolate such as this one with curves when you want to go from a large size to a small.

Browsers appear to typically use bi-linear (2x2 sampling) interpolation with the canvas element rather than bi-cubic (4x4 sampling) for (likely) performance reasons.

If the step is too huge then there are simply not enough pixels to sample from which is reflected in the result.

From a signal/DSP perspective you could see this as a low-pass filter's threshold value set too high, which may result in aliasing if there are many high frequencies (details) in the signal.

Solution

Update 2018:

Here's a neat trick you can use for browsers which supports the filter property on the 2D context. This pre-blurs the image which is in essence the same as a resampling, then scales down. This allows for large steps but only needs two steps and two draws.

Pre-blur using number of steps (original size / destination size / 2) as radius (you may need to adjust this heuristically based on browser and odd/even steps - here only shown simplified):

const canvas = document.getElementById("canvas");const ctx = canvas.getContext("2d");
if (typeof ctx.filter === "undefined") { alert("Sorry, the browser doesn't support Context2D filters.")}
const img = new Image;img.onload = function() {
// step 1 const oc = document.createElement('canvas'); const octx = oc.getContext('2d'); oc.width = this.width; oc.height = this.height;
// steo 2: pre-filter image using steps as radius const steps = (oc.width / canvas.width)>>1; octx.filter = `blur(${steps}px)`; octx.drawImage(this, 0, 0);
// step 3, draw scaled ctx.drawImage(oc, 0, 0, oc.width, oc.height, 0, 0, canvas.width, canvas.height);
}img.src = "//i.stack.imgur.com/cYfuM.jpg";
body{ background-color: ivory; }canvas{border:1px solid red;}
<br/><p>Original was 1600x1200, reduced to 400x300 canvas</p><br/><canvas id="canvas" width=400 height=250></canvas>

Can I turn off antialiasing on an HTML canvas element?

For images there's now context.imageSmoothingEnabled= false.

However, there's nothing that explicitly controls line drawing. You may need to draw your own lines (the hard way) using getImageData and putImageData.

Weird HTML 5 Canvas Antialiasing

After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.

After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.

In spite of my efforts, the lines are still there. Any suggestions?

Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/

HTML5 canvas disable antialias on primitive (curves, ...)

The imageSmoothingEnabledonly affects images drawn to canvas using drawImage() (hence the name imageSmoothingEnabled). There is sadly no way to disable this for lines and shapes.

As with 6502's answer and previous answers on SO you will need to implement "low-level" methods to achieve this.

Here are some links to some common algorithms for various primitives:

  • Bresenham line algorithm (see this answer for implementation)
  • Midpoint circle algorithm (and ellipse with the same) Here's an implementation.
  • Bezier curve (implementation at CodeProject)

The way you need to implement this is to get the x and y coordinates from these formulas. Then you need to either to use rect(x, y, 1, 1) or set a pixel directly by altering the image data buffer directly. The latter tend to be faster for these sort of things.

In both cases you will have a relatively huge performance reduction as you need to iterate using JavaScript versus browser's internal compiled iteration.



Related Topics



Leave a reply



Submit