javascripthtmlimagecanvashtml5-canvas

Resize image with javascript canvas (smoothly)


I'm trying to resize some images with canvas but I'm clueless on how to smoothen them. On photoshop, browsers etc.. there are a few algorithms they use (e.g. bicubic, bilinear) but I don't know if these are built into canvas or not.

Here's my fiddle: http://jsfiddle.net/EWupT/

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width=300
canvas.height=234
ctx.drawImage(img, 0, 0, 300, 234);
document.body.appendChild(canvas);

The first one is a normal resized image tag, and the second one is canvas. Notice how the canvas one is not as smooth. How can I achieve 'smoothness'?


Solution

  • You can use down-stepping to achieve better results. Most browsers seem to use linear interpolation rather than bi-cubic when resizing images.

    (Update There has been added a quality property to the specs, imageSmoothingQuality which is currently available in Chrome only.)

    Unless one chooses no smoothing or nearest neighbor the browser will always interpolate the image after down-scaling it as this function as a low-pass filter to avoid aliasing.

    Bi-linear uses 2x2 pixels to do the interpolation while bi-cubic uses 4x4 so by doing it in steps you can get close to bi-cubic result while using bi-linear interpolation as seen in the resulting images.

    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var img = new Image();
    
    img.onload = function () {
    
        // set size proportional to image
        canvas.height = canvas.width * (img.height / img.width);
    
        // step 1 - resize to 50%
        var oc = document.createElement('canvas'),
            octx = oc.getContext('2d');
    
        oc.width = img.width * 0.5;
        oc.height = img.height * 0.5;
        octx.drawImage(img, 0, 0, oc.width, oc.height);
    
        // step 2
        octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
    
        // step 3, resize to final size
        ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
        0, 0, canvas.width, canvas.height);
    }
    img.src = "//i.imgur.com/SHo6Fub.jpg";
    <img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234">
    <canvas id="canvas" width=300></canvas>

    Depending on how drastic your resize is you can might skip step 2 if the difference is less.

    In the demo you can see the new result is now much similar to the image element.