Search code examples
htmlcanvasantialiasingeaseljs

HTML5 Canvas image resize on Chrome & easeljs


I'm struggling to make smooth image resized in canvas in Chrome. In firefox it works well, but in Chrome I'm stuck on making it smooth.

Here is the jsfiddle http://jsfiddle.net/flashmandv/oxtrypmy/

var AVATAR_SIZE = 100;
var WHITE_BORDER_SIZE = 3;

var stage = new createjs.Stage("canvas");
var avCont = new createjs.Container(); 
stage.addChild(avCont);
avCont.x = avCont.y = 20;
//add white circle
var whiteBorderCircle = new createjs.Shape();
var radius = (AVATAR_SIZE+WHITE_BORDER_SIZE*2)/2;
whiteBorderCircle.graphics.beginFill("white").drawCircle(radius, radius, radius);
avCont.addChild(whiteBorderCircle);

//add avatar image mask
var avatarMask = new createjs.Shape();
avatarMask.graphics.beginFill("red").drawCircle(AVATAR_SIZE/2+WHITE_BORDER_SIZE, AVATAR_SIZE/2+WHITE_BORDER_SIZE, AVATAR_SIZE/2);

//add avatar image
var image = new Image();    
image.onload = function(){      
    var bitmap = new createjs.Bitmap(image);
    bitmap.mask = avatarMask;
    var bounds = bitmap.getBounds();
    bitmap.scaleX = (AVATAR_SIZE+WHITE_BORDER_SIZE*2) / bounds.width;
    bitmap.scaleY = (AVATAR_SIZE+WHITE_BORDER_SIZE*2) / bounds.height;
    avCont.addChild(bitmap);
    stage.update();
}; 
image.src = 'http://files.sharenator.com/sunflowers-s800x800-423444.jpg';

Notice the jagged image Please help


Solution

  • It is due to how clipping works in Chrome. Clip masks are pretty brutal in Chrome while in Firefox you get anti-aliasing along the non-straight edges.

    Here is a proof-of-concept for this (run this in Chrome and in FF to see the difference):
    http://jsfiddle.net/r65fcqoy/

    The only way to get around this is to use composite modes instead, which basically means you need to rewrite your code unless the library you're using support this in some way.

    One use of a composite mode is to use it to fill anything inside an existing drawing on the canvas.

    • We'll first create the filled circle we want the image to appear inside
    • Change comp mode to source-in and draw image
    • Then we go back to normal comp mode and draw the outer border

    Here is an approach using vanilla JavaScript where you can control how you plug things together - this is maybe not what you're after but there is really not much option if the library as said doesn't support comp mode instead of clipping:

    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d'),
        img = new Image,
        x = 70, y =70;
    
    var AVATAR_SIZE = 100;
    var WHITE_BORDER_SIZE = 3;
    var radius = (AVATAR_SIZE+WHITE_BORDER_SIZE*2)/2;
    
    img.onload = function() {
      
      // first draw the circle for the inner image:
      ctx.arc(x, y, AVATAR_SIZE*0.5, 0 , 2*Math.PI);
      ctx.fill();
      
      // now, change composite mode:
      ctx.globalCompositeOperation = 'source-in';
      
      // draw image in top
      ctx.drawImage(img, x-AVATAR_SIZE*0.5, y-AVATAR_SIZE*0.5,
                         AVATAR_SIZE, AVATAR_SIZE);
    
      // change back composite mode to default:
      ctx.globalCompositeOperation = 'source-over';
    
      // now draw border
      ctx.beginPath();
      ctx.arc(x, y, radius + 5, 0, 2*Math.PI);
      ctx.closePath();
      ctx.lineWidth = 10;
      ctx.strokeStyle = '#ffa94e';
      ctx.stroke();
    };
    img.src = 'https://i.sstatic.net/PB8lN.jpg';
    <canvas id=canvas width=500 height=180></canvas>