Reputation: 2500
I've finished hooking up a little full screen background overlay using the canvas tag but have hit a performance wall. What I've done is create a container called #OverlayPic and set it to 100% x 100%, display:none. Inside this container is my canvas element.
When fired, jQuery loads an image onto the canvas and gets the pixel info as array. The switch statement accepts the option the user sets for their desired filter effect. The code all works, but is horribly slow (and I think it's mainly due to how I've structured it, but I'm not sure of a better approach).
updateCanvas:function(onResize){
var img=new Image(); img.src=Main.ConSRC,
img.onload=function(){
var canvas=document.getElementById('Box_Canvas'),
ctx=canvas.getContext("2d"),
winW=$(window).width(), winH=$(window).height(),
imgW=this.width, imgH=this.height, smallestSide=Math.min(imgW,imgH);
// SETUP IMAGE PROPORTIONS
switch(smallestSide){
case imgW:
var width=winW,height=width*(imgW/imgH);
if(height < winH){ var height=winH, width=height*(imgW/imgH); };
break;
case imgH:
var height=winH,width=height*(imgW/imgH);
if(width < winW){ var width=winW, height=width*(imgH/imgW); };
break;
};
// DRAW IMAGE ON THE CANVAS
ctx.clearRect(0, 0, width*1.3, height*1.3 );
ctx.drawImage(img,0,0,width*1.3,height*1.3);
// IMAGE FILTERS
var imgdata=ctx.getImageData(0, 0, width, height), pix=imgdata.data, l=pix.length;
switch($this.data.bg_pic_filter){
// all filter code cases are here...
};
// APPLY THE FILER
ctx.putImageData(imgdata, 0, 0);
// FADE IN OVERLAY
if(!onResize){
Main.OBJ.$OverlayPic.fadeTo( $this.data.bg_pic_speed, $this.data.bg_pic_opacity);
};
};
},
This function is being called in 2 places.
When the user clicks an assigned element, the Overlay fades in and the canvas is loaded with the filtered image.
On the window resize event (onResize arg), in order to maintain the filter that was applied, otherwise it just defaults back to the original image?
Does anyone have any optimization suggestions? Thanks!
Upvotes: 2
Views: 696
Reputation: 6267
Well, you see, you have a giant image, and even if it's only 600x600, that's still 36,000 pixels, so even if your //all filter code cases are here...
has something like
case default:
var totalPixels = imagedata.data.length * .25; //divide by 4 now, since dividing is expensive compared to multiplication ( multiply by a decimal place is sometimes cheaper than dividing using /, most browsers have fixed this though[ this is important if you need to know multiple colors at once ] )
_data = imagedata.data
for( var i = totalPixels-1; i>=0; i-- ){
var index = i * 4 // this might be slower (creating a variable inside the loop) -- see next 2 lines
_data[i * 4] += 1 // these next 2 lines are identical
_data[index] += 1 // it might be faster to create an index, so you don't have to multiply, though usually multiplying is cheap and creating a variable inside a loop is expensive, so even if you have to multiple i * 4 a bunch, it might be faster than creating index
_data[index + 1] +=2 //green
_data[index + 2] +=2 //blue
_data[index + 3] +=2 //blue
}
so, as you see, you have do multiple 3600 times X 4 times(1 for each pixel)
this is where testing is important - compare identical things for performance gains in different browsers
dividing using / 4 is sometimes slower than multiplying by a decimal * .25 if you are dividing by a multiple of 2, such as x / 2, you can do x >> 1 or x << 1, called a bitwise shift, though some browsers have beefed up their multiplication so much that it is actually not faster to do this anymore(chrome)
That all being said, that is assuming you cannot use a web gl shader. See, so far, we have had a function to loop through each pixel, 1 at a time, through the processor, which is single threaded and slow.
In comes https://github.com/mrdoob/three.js/ -- THREE.js, which allows you to use shaders so you can render multiple pixels at a time, through the video card, which is really the only way to get more speed when you have to touch every pixel. This requires a webGL capable browser, which means you'll probably supporting canvas anyway, so hopefully this answer works.
Upvotes: 1