Kasper_Sky
Kasper_Sky

Reputation: 107

HTML5 Canvas: Invert Color of Pixels

I am following the tutorial on WC3 found here: http://www.w3schools.com/tags/canvas_getimagedata.asp

I have my image loaded up and next to this is the canvas which I have a nice boarder and I'm intending to display side-by-side images. I am attempting to invert the color of the pixel as the demonstration but I cannot get the colors to change I don't understand why.

Here is my code pen where I separated the HTML from the JS: http://codepen.io/kKasper/pen/zBXWOZ

document.getElementById("team").onload = function() {
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("team");
    ctx.drawImage(img, 0, 0);
    var imgData = ctx.getImageData(0, 0, c.width, c.height);
    // invert colors
    var i;
    for (i = 0; i < imgData.data.length; i += 4) {
        imgData.data[i] = 255 - imgData.data[i];
        imgData.data[i+1] = 255 - imgData.data[i+1];
        imgData.data[i+2] = 255 - imgData.data[i+2];
        imgData.data[i+3] = 255;
    }
    ctx.putImageData(imgData, 0, 0);
};

It appears that ctx.drawImage(img, 0, 0); is drawing my image inside the canvas. For some reason the function that works on the WC3 tutorial is not working properly on my image because when it draws the image after the function there are no changes.

Can anyone help me solve this please?

Upvotes: 8

Views: 14982

Answers (4)

Mike
Mike

Reputation: 17922

ctx.filter = 'invert(1)' is best but has limited browser support, so check if it is supported, otherwise use ctx.globalCompositeOperation = 'difference' which alone does not maintain transparency, but a clipping mask can be created and used to reset that. As some old browsers like IE do not support difference also check if that is supported and revert to using the ImageData and if the image does not have CORS support catch any error caused by tainted canvas.

 if (ctx.filter) {
    ctx.filter = 'invert(1)';
    ctx.drawImage(img, 0, 0);
  } else {
    ctx.drawImage(img, 0, 0);
    ctx.globalCompositeOperation = 'difference';
    if (ctx.globalCompositeOperation === 'difference') {
      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, img.width, img.height);
      var canvas2 = document.createElement('canvas');
      var ctx2 = canvas2.getContext('2d');
      canvas2.width = img.width;
      canvas2.height = img.height;
      ctx2.drawImage(img, 0, 0);
      ctx2.globalCompositeOperation = 'source-in';
      ctx2.fillStyle = 'black';
      ctx2.fillRect(0, 0, img.width, img.height);
      ctx.globalCompositeOperation = 'destination-in';
      ctx.drawImage(canvas2, 0, 0);
    } else {
      try {
        var imgData = ctx.getImageData(0, 0, img.width, img.height);
        var data = imgData.data;
        for (var i = 0; i < data.length; i += (i % 4 === 2 ? 2 : 1)) {
          data[i] = 255 - data[i];
        }
        ctx.putImageData(imgData, 0, 0);
      } catch (e) {}
    }
  }

Upvotes: 2

kuceb
kuceb

Reputation: 18043

Similar to markE's answer, but maintains transparency / alpha channel during draw.

ctx.filter = 'invert(1)'
ctx.drawImage(img, 0, 0)

There's loads of other filters you can apply as well

if you want to save to a base64 data url, you can use the following after drawing to your canvas

const dataUrl = canvas.toDataURL()

If you're getting error messages about a tainted canvas, before you set your image.src, set the crossOrigin property to 'Anonymous':

const img = new Image()
img.crossOrigin = 'Anonymous'
img.onload = myLoadFn
img.src = myImgUrl

Upvotes: 6

markE
markE

Reputation: 105015

You can use compositing to invert your image as long as your browser supports blending.

Advantages:

  • This is faster than getImageData
  • The canvas won't object with cross-origin security restrictions

enter image description here

ctx.drawImage(img,0,0);
ctx.globalCompositeOperation='difference';
ctx.fillStyle='white';
ctx.fillRect(0,0,canvas.width,canvas.height);

Upvotes: 26

qxz
qxz

Reputation: 3854

Try opening your browser's developer console on your Codepen. I'm getting this error:

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.

This means that you've drawn an image from another domain (i.e. not codepen.io) onto the canvas. As a security feature to prevent malicious scripts from communicating with external domains, browsers will disallow JS from accessing such data. See the MDN article on the same-origin policy.

Try running your code from a local file, or using an image from the same domain as the page. I bet it will work.

The dev console is your friend!

Upvotes: 0

Related Questions