Noitidart Noitidart - 7 months ago 40
Javascript Question

Pixelate Image Data Algorithm very slow

I had this pixelate algorithm in my tools, but when I came to apply it today to my drawing app, it's performance is seriously bad. I was wondering if you could help me with this.

This is the algo I had:

//apply pixalate algorithm
for(var x = 1; x < w; x += aOptions.blockSize)
{
for(var y = 1; y < h; y += aOptions.blockSize)
{
var pixel = sctx.getImageData(x, y, 1, 1);
dctx.fillStyle = "rgb("+pixel.data[0]+","+pixel.data[1]+","+pixel.data[2]+")";
dctx.fillRect(x, y, x + aOptions.blockSize - 1, y + aOptions.blockSize - 1);
}
}


I was wondering if you could help me speed it up, I'm not sure whats causing this perf hit.

(yes i know the imageSmoothingEnabled techqnique however its not giving me perfect control over the block size)

Answer

You fetch an ImageData object for each pixel, then construct a colour string which has to be parsed again by the canvas object and then use a canvas fill routine, which has to go through all the canvas settings (transformation matrix, composition etc.)

You also seem to paint rectangles that are bigger than they need to be: The third and fourth parameters to fillRect are the width and height, not the x and y coordinated of the lower right point. An why do you start at pixel 1, not at zero?

It is usually much faster to operate on raw data for pixel manipulations. Fetch the whole image as image data, manipulate it and finally put it on the destination canvas:

var idata = sctx.getImageData(0, 0, w, h);
var data = idata.data;

var wmax = ((w / blockSize) | 0) * blockSize;
var wrest = w - wmax;

var hmax = ((h / blockSize) | 0) * blockSize;
var hrest = h - hmax;

var hh = blockSize;

for (var y = 0; y < h; y += blockSize) {
    var ww = blockSize;
    if (y == hmax) hh = hrest;

    for (var x = 0; x < w; x += blockSize) {
        var n = 4 * (w * y + x);
        var r = data[n];
        var g = data[n + 1];
        var b = data[n + 2];
        var a = data[n + 3];

        if (x == wmax) ww = wrest;

        for (var j = 0; j < hh; j++) {
            var m = n + 4 * (w * j);

            for (var i = 0; i < ww; i++) {
                data[m++] = r;
                data[m++] = g;
                data[m++] = b;
                data[m++] = a;
            }
        }
    }
}

dctx.putImageData(idata, 0, 0);

In my browser, that's faster even with the two additonal inner loops.

Comments