Canvas Image Filters

Alpha Composites

When drawing shapes on an HTML5 canvas, we can set how what we draw is composited with what is already drawn on the canvas.

The canvas 2D Context has two attributes which control the composition mode of a canvas. These are:

  1. globalAlpha
  2. globalCompositeOperation

GlobalAlpha

Example of a global alpha canvas (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
    height:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let originalImage = null;
let width = null;
let height = null;


window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    originalImage = document.getElementById('originalImage');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // set the alpha
    ctx.globalAlpha = 0.25;

    // draw an image using the alpha
    ctx.drawImage(originalImage, 0, 0, width, height);

    // reset the alpha
    ctx.globalAlpha = 1.0;

    // draw an image using the reset alpha
    ctx.drawImage(originalImage, width - 100, height - 100, 100, 100);
}
</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html> 

Write code to place a faded copyright text on a canvas image.

Composites

We use composites to blend new content with what is already drawn on a canvas. There are 12 composite operations, as shown below:

source-atop
Where source and destination overlaps, the source is displayed. Where no overlaps exists, or where the source is transparent, the destination is displayed.
source-in
Where source and destination overlaps and both are opaque, the source is displayed. Everywhere else transparency is displayed.
source-out
Where the source and destination do not overlap, the source is displayed. Everwhere else transparency is displayed.
source-over
Where the source is opaque, the source is displayed. The destination is displayed everywhere else.
destination-atop
Where the source and destination overlap, and both are opaque, the destination is displayed. Where the destination is transparent, the source is displayed.
destination-in
Where the source and destination overlaps, and both are opaque, the destination is displayed. The source is not displayed where no overlaps exists.
destination-out
Displays the destination everywhere the source and destination do not overlap. Everywhere else, transparency is displayed.
destination-over
Where source and destination overlaps, the destination is displayed. Where no overlap exists, the source is displayed.
lighter
The source and destination colors are added to each other, resulting in brighter colors, moving towards colour values of 1 (maximum brightness for that color).
darker
The source and destination colors are subtacted from each other, resulting in brighter colors, moving towards colour values of 0 (minimum brightness for that color).
xor
Shapes are made transparent where both overlap and drawn normal everywhere else.
copy
Where source and destination overlaps, the source is displayed.

Perhaps the most useful alpha composite is the 'source-in composite. It can be used to show part of one image on top of another image.

Example of a source-in alpha composite region (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let width = null;
let height = null;
let originalImage = null;


window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    originalImage = document.getElementById('originalImage');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // 1) define the alpha area  
    ctx.beginPath();
    ctx.fillStyle = "red"; // any colour can be used
    ctx.fillRect(100, 100, 200, 100);
    ctx.closePath();

    // 2) select the alpha composite
    ctx.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    ctx.drawImage(originalImage, 0, 0, width, height);
}
</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>

 

Write code to display a circular (spotlight) image on a full-sized, faded, background image, (Example Solution).

Expand your code so that the user can drag the spotlight around the canvas (Example Solution).

Expand your code so that the user can drag a magnified spotlight around the canvas (Example Solution).

List a use for each of the 12 various alpha composites.

Modify the code above so that the user can select any of the 12 alpha composites (Example Solution).

Example of a source-in alpha composite image (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let originalImage = null;
let width = null;
let height = null;
let alphaImage = new Image();
alphaImage.src = 'images/four_leaf_clover.png';

window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    originalImage = document.getElementById('originalImage');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // 1) define the alpha area   
    ctx.drawImage(alphaImage, 100, 50, 200, 200);

    // 2) select the alpha composite
    ctx.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    ctx.drawImage(originalImage, 0, 0, width, height);
}
</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html> 

Modify the above code to use a different image.

Example of a source-in alpha composite text (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let width = null;
let height = null;
let originalImage = null;


window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    originalImage = document.getElementById('originalImage');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // 1) define the alpha area   
    ctx.beginPath();
    ctx.fillStyle = "red";
    ctx.font = "170px Times Roman";
    ctx.fillText("DkIT", 25, 200);
    ctx.closePath();

    // 2) select the alpha composite
    ctx.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    ctx.drawImage(originalImage, 0, 0, width, height);
}
</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>

 

Change the text font and position in the code above.

Allow the user to type in the text that will be used as the alpha composite.

Use the global alpha to fade your modified code.

Complex Alpha Composites

Example of a drop shadow on an image (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let width = null;
let height = null;
let originalImage = null;

let alphaImage = new Image();
alphaImage.src = 'images/four_leaf_clover.png';

window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    originalImage = document.getElementById('originalImage');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // set up a double-buffer
    let doubleBuffer = document.createElement('canvas');
    let doubleBufferG = doubleBuffer.getContext('2d');
    doubleBuffer.width = width;
    doubleBuffer.height = height;

    // define the shadow offset and colour
    let offset = 10;
    let shadowColour = '#ff0';

    // 1) define the alpha area    
    doubleBufferG.drawImage(alphaImage, 10, 10, 350, 350);

    // 2) select the alpha composite   
    doubleBufferG.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    doubleBufferG.beginPath();
    doubleBufferG.fillStyle = shadowColour;
    doubleBufferG.fillRect(0, 0, width, height);
    doubleBufferG.closePath();

    // draw the saved shadow image onto the canvas slightly below and to the right 
    // of where the actual image will be drawn
    ctx.drawImage(doubleBuffer, 0, 0, width + offset, height + offset);

    // draw the image slightly above and to the left of the shadow
    // this is similar to the code above, except now we draw the
    // original image rather than its shadow
    doubleBufferG.globalCompositeOperation = 'source-over';

    // 1) define the alpha area
    doubleBufferG.drawImage(alphaImage, 10, 10, 350, 350);

    // 2) select the alpha operation    
    doubleBufferG.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    doubleBufferG.drawImage(originalImage, 0, 0, width, height);

    // draw the saved image onto the canvas slightly below and to the right 
    // of where the actual image will be drawn   
    ctx.drawImage(doubleBuffer, 0, 0, width, height);
}



</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>
 

Change the shadow colour to grey in the example above.

In real shadows, the shadow colour fades out as it moves away from the object. Modify the code above, so that the shadow has ten different shades of grey (Example solution).

Replace the overlay image with text in the above example.

Example of a multi-layered image (Run Example)

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Worked example from lecture notes</title>
<style>
img
{
    width:400px;
}

#loadingMessage
{
    position:absolute;
    top:100px;
    left:100px;
    z-index:100;
    font-size:50px;
}
</style>

<script>
let canvas = null;
let ctx = null;
let width = null;
let height = null;
let originalImage = null;
let alphaImage = new Image();
alphaImage.src = 'images/overlay.png';
let pictureFrame = new Image();
pictureFrame.src = 'images/frame.png';


window.onload = onAllAssetsLoaded;
document.write("<div id='loadingMessage'>Loading...</div>");
function onAllAssetsLoaded()
{
    // hide the webpage loading message
    document.getElementById('loadingMessage').style.visibility = "hidden";

    originalImage = document.getElementById('originalImage');
    canvas = document.getElementById('canvas');
    ctx = canvas.getContext('2d');
    width = originalImage.clientWidth;
    height = originalImage.clientHeight;
    canvas.width = width;
    canvas.height = height;

    renderCanvas();
}


function renderCanvas()
{
    // 1) define the alpha area   
    ctx.drawImage(alphaImage, 0, 0, width, height);

    // 2) select the alpha composite
    ctx.globalCompositeOperation = 'source-in';

    // 3) draw the original image
    // only the part that overlaps the alpha area will be visible
    ctx.drawImage(originalImage, 0, 0, width, height);

    // draw the picture frame on top of the picture
    ctx.globalCompositeOperation = 'source-over';
    ctx.drawImage(pictureFrame, 0, 0, width, height);
}
</script>
</head>

<body>
<img id = 'originalImage' src = 'images/dancing.png'>
<canvas id = 'canvas'></canvas>
</body>
</html>

Modify the above code to display a different photo frame.

 
<div align="center"><a href="../../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>