简体   繁体   中英

Performance issue with canvas.getImageData + canvas.putImageData

I'm calling getImageData/putImageData a lot and it causes my Chrome process to fill up until it crashes when it reaches 2.5Gb memory.

I'm using simpleheat.js with a leaflet heat map plugin to display heatmap on a canvas.

Basically, I'm calling the draw fn a lot to establish a "heatmap over time" effect. The data I deal with each run (each getImageData/putImageData) is 4Mb, while called at 10-20 fps.

I'm wondering how I can optimize this code. I know very little about working with canvas.

This is the relevant code from simpleheat.js :

draw: function() {
    ...
    var colored = ctx.getImageData(0, 0, this._width, this._height);
    this._colorize(colored.data, this._grad);
    ctx.putImageData(colored, 0, 0);
},
_colorize: function (pixels, gradient) {
    for (var i = 0, len = pixels.length, j; i < len; i += 4) {
        j = pixels[i + 3] * 4; // get gradient color from opacity value

        if (j) {
            pixels[i] = gradient[j];
            pixels[i + 1] = gradient[j + 1];
            pixels[i + 2] = gradient[j + 2];
        }
    }
}

I've tried reducing DOM access (suggested in another question)

var colored = ctx.getImageData(0, 0, this._width, this._height);
var coloredData = colored.data;
this._colorize(coloredData, this._grad);
colored.data = coloredData;
ctx.putImageData(colored, 0, 0);

I think the issue is that the browser doesn't release the 4Mb of data properly since if I keep the page for a while the memory will eventually drop

I've tried some silly attempts to help the GC release the data array ( colored.data = null or undefined )

When creating the ctx, you can give it an attribute which makes these operations faster.

const ctx = canvas.getContext("2d", { willReadFrequently: true }); More about willReadFrequently: HTML willReadFrequently specifications (If you scroll around there are some other tidbits about canvas that might be useful to performance)

Also, if possible, avoid running getImageData every frame. You can have one image that you can change and place every frame, if there is a way to get the opacity value for the gradient without getting the values of every pixel.

You could also just forget this method entirely and do the same using gpu.js, which wouldn't be too difficult looking at the implementation.

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