Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do i tint an image with HTML5 Canvas?

Tags:

html

canvas

2d

My question is, what is the best way to tint an image that is drawn using the drawImage method. The target useage for this is advanced 2d particle-effects (game development) where particles change colors over time etc. I am not asking how to tint the whole canvas, only the current image i am about to draw.

I have concluded that the globalAlpha parameter affects the current image that is drawn.

//works with drawImage() canvas2d.globalAlpha = 0.5; 

But how do i tint each image with an arbitrary color value ? It would be awesome if there was some kind of globalFillStyle or globalColor or that kind of thing...

EDIT:

Here is a screenshot of the application i am working with: http://twitpic.com/1j2aeg/full alt text http://web20.twitpic.com/img/92485672-1d59e2f85d099210d4dafb5211bf770f.4bd804ef-scaled.png

like image 505
djdolber Avatar asked Apr 22 '10 07:04

djdolber


2 Answers

You have compositing operations, and one of them is destination-atop. If you composite an image onto a solid color with the 'context.globalCompositeOperation = "destination-atop"', it will have the alpha of the foreground image, and the color of the background image. I used this to make a fully tinted copy of an image, and then drew that fully tinted copy on top of the original at an opacity equal to the amount that I want to tint.

Here is the full code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html>     <head>         <title>HTML5 Canvas Test</title>         <script type="text/javascript"> var x; //drawing context var width; var height; var fg; var buffer  window.onload = function() {     var drawingCanvas = document.getElementById('myDrawing');     // Check the element is in the DOM and the browser supports canvas     if(drawingCanvas && drawingCanvas.getContext) {         // Initaliase a 2-dimensional drawing context         x = drawingCanvas.getContext('2d');         width = x.canvas.width;         height = x.canvas.height;          // grey box grid for transparency testing         x.fillStyle = '#666666';         x.fillRect(0,0,width,height);         x.fillStyle = '#AAAAAA';         var i,j;         for (i=0; i<100; i++){             for (j=0; j<100; j++){                 if ((i+j)%2==0){                     x.fillRect(20*i,20*j,20,20);                 }             }         }          fg = new Image();         fg.src = 'http://uncc.ath.cx/LayerCake/images/16/3.png';          // create offscreen buffer,          buffer = document.createElement('canvas');         buffer.width = fg.width;         buffer.height = fg.height;         bx = buffer.getContext('2d');          // fill offscreen buffer with the tint color         bx.fillStyle = '#FF0000'         bx.fillRect(0,0,buffer.width,buffer.height);          // destination atop makes a result with an alpha channel identical to fg, but with all pixels retaining their original color *as far as I can tell*         bx.globalCompositeOperation = "destination-atop";         bx.drawImage(fg,0,0);           // to tint the image, draw it first         x.drawImage(fg,0,0);          //then set the global alpha to the amound that you want to tint it, and draw the buffer directly on top of it.         x.globalAlpha = 0.5;         x.drawImage(buffer,0,0);     } }         </script>     </head>     </body>         <canvas id="myDrawing" width="770" height="400">             <p>Your browser doesn't support canvas.</p>         </canvas>     </body> </html> 
like image 51
Nathan Avatar answered Sep 26 '22 11:09

Nathan


There is a method here you can use to tint images, and it's more accurate then drawing coloured rectangles and faster then working on a pixel-by-pixel basis. A full explanation is in that blog post, including the JS code, but here is a summary of how it works.

First you go through the image you are tinting pixel by pixel, reading out the data and splitting each pixel up into 4 separate components: red, green, blue and black. You write each component to a separate canvas. So now you have 4 (red, green, blue and black) versions of the original image.

When you want to draw a tinted image, you create (or find) an off-screen canvas and draw these components to it. The black is drawn first, and then you need set the globalCompositeOperation of the canvas to 'lighter' so the next components are added to the canvas. The black is also non-transparent.

The next three components are drawn (the red, blue and green images), but their alpha value is based on how much their component makes up the drawing colour. So if the colour is white, then all three are drawn with 1 alpha. If the colour is green, then only the green image is drawn and the other two are skipped. If the colour is orange then you have full alpha on the red, draw green partially transparent and skip the blue.

Now you have a tinted version of your image rendered onto the spare canvas, and you just draw it to where ever you need it on your canvas.

Again the code to do this is in the blog post.

like image 42
JL235 Avatar answered Sep 25 '22 11:09

JL235