I'm attempting to load a full-screen background image onto a canvas, then apply an image filter to it, but have a few questions. I'm running into severe performance issues, but only when resizing the window. Basically, my code attempts to keep the canvas/image matching the screen dimensions (which works well).
UPDATE CANVAS METHOD:
The updateCanvas method is called on load to initially create/load the image object and place it on the canvas. If a filter is selected, it calls the filter method. It accepts the onResize argument, which is passed on window resize to scale the canvas/image. MAIN refers to an object used for referencing elements.
updateCanvas:function(onResize){
// SETUP VARIABLES
var img=new Image(),
$this=Main.currentOBJ, // REFERENCE FOR .DATA
objData=$this.data,
canvas=Main.OBJ.$Canvas[0],
ctx=canvas.getContext("2d"),
winW=$(window).width(), winH=$(window).height();
// SOURCE CAN BE SET IN .DATA OR DEFAULT
img.src=(objData.bg_pic_src!=='') ? objData.bg_pic_src : Main.ConSRC;
// LOAD THE IMAGE OBJECT
img.onload=function(){
var imgW=img.width, imgH=img.height,
ratio=imgW/imgH, newW=winW, newH=Math.round(newW/ratio);
// SETUP IMAGE PROPORTIONS
if(newH < winH){ var newH=winH, newW=Math.round(newH*ratio); };
// WHEN RESIZING THE BROWSER
if(!onResize){ // INTIAL DRAW
Main.OBJ.$Canvas.attr({'width':newW+'px', 'height':newH+'px'});
ctx.drawImage(img,0,0,newW,newH);
// APPLY FILTERS
if(objData.bg_pic_filter > 0){
Main.canvasFilter(ctx,newW,newH); // FILTER METHOD
}else{
Main.OBJ.$OverlayPic.animate({opacity:parseFloat(objData.bg_pic_opacity,10)},
{duration:objData.bg_pic_speed_in,queue:false});
};
}else{ // RESIZING
Main.OBJ.$Canvas.attr({'width':newW+'px', 'height':newH+'px'});
ctx.drawImage(img,0,0,newW,newH);
if(objData.bg_pic_filter > 0){
Main.canvasFilter(ctx,newW,newH); // FILTER METHOD
};
};
};
The Canvas Filter Method: If an image filter is to be applied to the background image, the canvasFilter method is called from the canvasUpdate method.
canvasFilter:function(ctx,width,height){
// SETUP VARIABLES
var objData=Main.currentOBJ.data,
canvasWidth=width, canvasHeight=height,
imgdata=ctx.getImageData(0, 0, canvasWidth, canvasHeight),
pix=imgdata.data, l=pix.length;
// APPLY THE CORRECT LOOP DEPENDING UPON THE FILTER NUMBER
switch(objData.bg_pic_filter){
case 1: ... break;
case 2:
for(var i=l; i>0; i-=4){
var cacher=pix[i+2];
pix[i]=pix[i+1];
pix[i+1]=cacher;
pix[i+2]=cacher;
pix[i+3]=cacher;
};
break;
};
// APPLY THE FILER & FADE IN THE BACKGROUND
ctx.putImageData(imgdata, 0, 0);
Main.OBJ.$OverlayPic.fadeTo(parseInt(objData.bg_pic_speed_in,10),
parseFloat(objData.bg_pic_opacity,10));
};
All of this works, and the initial draw/filter are quite fast. However, when I resize the window it's very sluggish. I've temporarily gotten around this by putting in a throttler, which just sets a timer to avoid firing the above functions too quickly. This helps slightly, but will keep the background image in place (displaying open solid background areas around the image) until the throttler timer kicks in and fires the methods - resizing the canvas/image and applying the filter.
Questions:
Is there a faster way? I've read about using Typed Arrays/Bitwise for pixel manipulation loops, but it seems that support is horrible for this. I've also looked into CSS3 filters to accomplish the same thing, but again, support is horrible. So, would this be the only approach to full-screen background image filters with "modern browser" compatibility?
The updateCanvas method is creating a new image object each time, I'm thinking that this might be a bottleneck, but am unsure, and also unsure of how to get around it?
I've read others using a separate canvas as a buffer? Would this actually increase performance in my situation?
I'm really thinking there's a major logic issue in my code somewhere. It just doesn't make sense to me that I would need to loop through all of the pixel information more than once, but this is the only way I've gotten it to work. Isn't there a way I can draw it/apply the filter once, then on resize, just adjust the dimensions without having to redraw/reloop/reapply the filter? I would think that it would maintain the filtered image, but if I remove the call to the canvasFilter method on window resize, it completely removes the filter effect.
I've yet to use memoization techniques as I've never quite understood it, and never quite understand where/when to use it. But, essentially, if I have a function that is returning the same results again and again (such as the canvasFilter method) can I memoize to increase performance - or would it be difference because it's pulling different pixel information after the window has resized?
I've also heard that a webGL render might help with this? Might be way off as I don't know anything about webGL.
So sorry for the loooonng question, but hopefully this covers some topics that others can benefit from as well. Any suggestions/tips would be much appreciated. Thanks! :)