Html5 Canvas Drawimage: How to Apply Antialiasing

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>

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?

HTML Canvas: draw image without anti-aliasing

The only way to convert an image into an ImageData object is to draw it to a canvas first, so you'll need to create a temporary canvas, draw the image on it, and get the image data from there.

function imageToImageData(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, canvas.width, canvas.height);
}

Note though, that the same-origin policy prevents you from calling getImageData if you draw an image from a different domain to the canvas, so this will only work on images from the same domain as the document. If you need to draw images from other domains, your only option is to call drawImage on the context for you main canvas directly, making sure there are no transformations that will affect the accuracy.

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/

Canvas DrawImage() poor quality

Sample Image

You can incrementally scale your image down for better results.

Since your final size is 1/4 the original size, you could:

  • scale the 1000x669 image in half to 500x334 onto a temp canvas

  • scale the 500x335 canvas in half to 250x167 onto the main canvas

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var img=new Image();
img.onload=start;
img.src="https://ictcluster.ge/wp-content/uploads/2018/09/wandio-logo-1-1024x724.png";
function start(){


// scale the 1000x669 image in half to 500x334 onto a temp canvas
var c1=scaleIt(img,0.50);

// scale the 500x335 canvas in half to 250x167 onto the main canvas
canvas.width=c1.width/2;
canvas.height=c1.height/2;
ctx.drawImage(c1,0,0,250,167);

}

function scaleIt(source,scaleFactor){
var c=document.createElement('canvas');
var ctx=c.getContext('2d');
var w=source.width*scaleFactor;
var h=source.height*scaleFactor;
c.width=w;
c.height=h;
ctx.drawImage(source,0,0,w,h);
return(c);
}
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

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