Search code examples
javascripthtmlcanvastransparencypixel

putImageData(), how to keep old pixels if new pixels are transparent?


In html5, when you draw to a canvas using putImageData(), if some of the pixels you are drawing are transparent (or semi-transparent), how do you keep old pixels in the canvas unaffected?

example:

var imgData = context.createImageData(30,30);
for(var i=0; i<imgData.data.length; i+=4)
{
imgData.data[i]=255;
imgData.data[i+1]=0;
imgData.data[i+2]=0;
imgData.data[i+3]=255;
if((i/4)%30 > 15)imgData.data[i+3] = 0;
}
context.putImageData(imgData,0,0);

The right half of the 30x30 rect is transparent. If this is drawn over something on the canvas, pixels behind the right half are removed (or become thransparent). How do I keep them?


Solution

  • You can use getImageData to create a semi-transparent overlay:

    • create a temporary offscreen canvas
    • getImageData to get the pixel data from the offscreen canvas
    • modify the pixels as you desire
    • putImageData the pixels back on the offscreen canvas
    • use drawImage to draw the offscreen canvas to the onscreen canvas

    enter image description here

    Here's example code and a Demo: http://jsfiddle.net/m1erickson/CM7uY/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <style>
        body{ background-color: ivory; }
        canvas{border:1px solid red;}
    </style>
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var context=canvas.getContext("2d");
    
        // draw an image on the canvas
        var img=new Image();
        img.onload=start;
        img.src="https://dl.dropboxusercontent.com/u/139992952/stack1/landscape1.jpg";
        function start(){
            canvas.width=img.width;
            canvas.height=img.height;
            context.drawImage(img,0,0);
    
            // overlay a red gradient 
            drawSemiTransparentOverlay(canvas.width/2,canvas.height)
    
        }
    
        function drawSemiTransparentOverlay(w,h){
    
            // create a temporary canvas to hold the gradient overlay
            var canvas2=document.createElement("canvas");
            canvas2.width=w;
            canvas2.height=h
            var ctx2=canvas2.getContext("2d");
    
            // make gradient using ImageData
            var imgData = ctx2.getImageData(0,0,w,h);
            var data=imgData.data;
            for(var y=0; y<h; y++) {
                for(var x=0; x<w; x++) {
                    var n=((w*y)+x)*4;
                    data[n]=255;
                    data[n+1]=0;
                    data[n+2]=0;
                    data[n+3]=255;
                    if(x>w/2){
                        data[n+3]=255*(1-((x-w/2)/(w/2)));
                    }
                }
            }
    
            // put the modified pixels on the temporary canvas
            ctx2.putImageData(imgData,0,0);
    
            // draw the temporary gradient canvas on the visible canvas
            context.drawImage(canvas2,0,0);
    
        }
    
    
    }); // end $(function(){});
    </script>
    </head>
    <body>
        <canvas id="canvas" width=200 height=200></canvas>
    </body>
    </html>
    

    Alternatively, you might check out using a linear gradient to do your effect more directly.

    http://jsfiddle.net/m1erickson/j6wLR/