简体   繁体   中英

Canvas radial gradient vs .png performance

I'm trying to simulate brushes with the HTML canvas element. To get brush hardness, I'm using a radial gradient, but I'm not entirely sure whether it's faster to create a new radial gradient for every point or saving the radial gradient as an image and use drawImage() instead.

Current code:

var gradient = context.createRadialGradient(x, y, hardness * brushSize, x, y, brushSize);

gradient.addColorStop(0, color);
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');

context.fillStyle = gradient;
context.fillRect(x - halfBrushSize, y - halfBrushSize, brushSize, brushSize);

drawImage (apart from creating the image):

context.drawImage(img, x, y);

Gradients are expensive to generate contrary to images which are basically copies. They both need to go through transformation matrix and anti-aliasing process though, but there is no calculation involved with images besides from that.

UPDATE From the comments below people seem to get extremely variable test results depending on browser and hardware. The embedded test is not very accurate and was meant as a pointer, so for this reason I created a more accurate test here . Feel free to post results below in comments.
-- update end --

The following is not the world's most accurate test, but the difference is so large that you get a pretty good pointer in any case to which is faster:

 window.performance = window.performance || Date; setTimeout(go, 250); function go() { var ctx = c.getContext("2d"); // create radial gradient var gr = ctx.createRadialGradient(300, 300, 300, 300, 300, 0); gr.addColorStop(0, "#000"); gr.addColorStop(1, "#f00"); ctx.fillStyle = gr; // test gradient fill style var time1 = performance.now(); for (var i = 1000; i--;) ctx.fillRect(0, 0, c.width, c.height); var time2 = performance.now() - time1; o.innerHTML = "Gradient: " + time2.toFixed(4) + "<br>"; // test cached gradient (canvas = image source) ctx = c2.getContext("2d"); time1 = performance.now(); for (i = 1000; i--;) ctx.drawImage(c, 0, 0); time2 = performance.now() - time1; o.innerHTML += "drawImage: " + time2.toFixed(4); } 
 <output id=o>Running... please wait</output><br> <canvas id=c width=600 height=600></canvas><br> <canvas id=c2 width=600 height=600></canvas> 

When it comes to render a radial gradient, you can build the gradient on-the-fly, or use a png as you quoted, yet there's a third possibility : you can use a normalised gradient, that you build once, then use at will at any place/size by using the context transforms.

The code used to create the normalized gradient for a given hardness looks like :

var mySingleGradient =  ctx.createRadialGradient(0.5, 0.5, 0.5*hardness, 0.5, 0.5, 0.5);
mySingleGradient.addColorStop(0, color);
mySingleGradient.addColorStop(1, '#000');

Just like when you are using png, you'll run into the issue of caching the gradients for any base color + hardness. But you won't have any png resolution issue, and most probably the size of the gradients will be way smaller than the png's.

You use such a normalised gradient with :

function drawThatGradientHere(ctx, x, y, gradient, brushSize) {
    ctx.save();
    ctx.translate(x,y);
    ctx.scale(brushSize,brushSize);
    ctx.fillStyle = gradient;
    ctx.fillRect(0,0,1,1);
    ctx.restore();
}

I won't go into benchmarking, since there are too many chances to compare apples and oranges without knowing more about the use. Because for instance, the drawImage might very well perform very differently if you are using its scaled version. Mind also that by using an image, you might run into resolution issues (too high : perf, too low : aliasing), that you won't have if you are using a gradient. So even if the gradient was proved slower, you might prefer it because of the way it consistently looks.

A few questions : do you change your hardness often ? do you change the brush size often ? do you change the start/end color of your gradient ?

It's only by answering those question and having a random set of rect/hardness that has the same average distribution of your real use case that you'll be able to benchmark/compare anything.

Last word : If it's becoming hard to say which solution is faster, its time to pick the solution relying on... some other good reason... :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM