Search code examples
javascripthtmlcanvascomposition

HTML Canvas composition: combining "lighter" and "source-atop" effects


Essentially, I have a gray-scale image of a cube that I would like to color different colors using an HTML 5 canvas. I don't care much about browser compatibility at the moment, so I've been looking at the globalCompositeOperation property values listed here:

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

Essentially, I want to combine the effects of "source-atop" and "lighter". Using just source-atop, I get a blue shape, but the different shades of gray are all filled in with the same shade of blue, so I get a flat, skewed hexagon instead of a cube.

Using just the lighter composite option, I get closer to the effect I want. All the cube faces are differing shades of blue like I want, but the previously transparent background becomes solid blue.

I would love a canvas solution that would produce the cube in the lighter example without the blue background. I realize I could just define the cube's points and use the fill style and paths to create the cube, but I have plans to use more complex icon shapes than a cube, and I don't want to do all that when I already have a gray-scale .png ready unless I absolutely have to.

Code currently is pretty basic

var canvas = document.getElementsByTagName("canvas")[0];
var ctx = canvas.getContext("2d");
var cube = new Image();
cube.src = "url" //path to gray-scale cube image.

ctx.clearRect(0, 0, canvas.width, canvaseheight);
ctx.drawImage(cube, 0, 0);
ctx.globalCompositeOperation = "lighter"; //or "source-atop"
ctx.fillStyle = "blue";
ctx.fillRect(0, 0, canvas.width, canvas.height);

Solution

  • Turns out you can combine them pretty easily. Here is how I ended up accomplishing this task.

    var canvas = document.getElementsByTagName("canvas")[0];
    var ctx = canvas.getContext("2d");
    var cube = new Image();
    cube.src = "url" //path to gray-scale cube image.
    
    //get a blue mask that fills the entire cube region
    ctx.clearRect(0, 0, canvas.width, canvaseheight);
    ctx.drawImage(cube, 0, 0);
    ctx.globalCompositeOperation = "source-atop";
    ctx.fillStyle = "blue";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    //save that image off somewhere.
    var blueMask = new Image();
    blueMask.src = canvas.toDataURL();
    
    ctx.clearRect(0,0,canvas.width, canvas.height);
    ctx.globalCompositeOperation = "source-over";
    
    //draw the cube again
    ctx.drawImage(cube, 0,0);
    
    //draw the mask image over it with the 'lighter' composition setting.
    ctx.globalCompositeOperation = "lighter";
    ctx.drawImage(blueMask, 0, 0);
    

    This produces the desired result.