Adam Burucs
Adam Burucs

Reputation: 1

Canvas double buffering for a fire effect

I'd like to make a simple fire effect using Canvas. At the moment I don't understand the coding aspect of using a double buffer in THIS context (error). I made the code using Brackets and the JSLint code has passed well. Error is "Cannot set property 'NaN' of undefined." What am I doing wrong in this example?

/*global document: false */
/*global setInterval */

function getPixel(imageData, x, y) {
    "use strict";
    var index = 0, r, g, b;

    index = (x + y * imageData.width) * 4;
    r = imageData.data[index];
    g = imageData.data[index + 1];
    b = imageData.data[index + 2];

    return [r, g, b];
}

function setPixel(imageData, x, y, r, g, b, a) {
    "use strict";
    var index = 0;
    index = (x + y * imageData.width) * 4;
    imageData.data[index]     = r;
    imageData.data[index + 1] = g;
    imageData.data[index + 2] = b;
    imageData.data[index + 3] = a;
}

var element = document.getElementById("canvas");
var c       = element.getContext("2d");
var w       = element.width;
var h       = element.height;

// set buffer
var offScreenCanvas         = document.createElement('canvas');
offScreenCanvas.width       = w;
offScreenCanvas.height      = h;
var ctxOffscreen            = offScreenCanvas.getContext('2d');

function init() {
    "use strict";
    var x, y, i, j, r, g, b;

    for (j = 0; j < 10; j += 1) {
        for (i = 0; i < 800; i += 1) {
            x = i;
            y = j;
            r = 255 - Math.round(Math.random() * 50);
            g = 0;
            b = 0;
            setPixel(ctxOffscreen, x, y, r, g, b, 255);
        }
    }

    c.putImageData(ctxOffscreen, 0, 0);
}

function animation() {
    "use strict";
    var a1, a2, a3, a4, r, g, b, i, j;

    // draw
    for (j = 9; j < 100; j += 1) {
        for (i = 0; i < 800; i += 1) {
            a1 = getPixel(offScreenCanvas, i, j - 1);
            a2 = getPixel(offScreenCanvas, i, j + 1);
            a3 = getPixel(offScreenCanvas, i - 1, j);
            a4 = getPixel(offScreenCanvas, i + 1, j);

            r = Math.round((a1[0] + a2[0] + a3[0] + a4[0]) / 4);
            g = Math.round((a1[1] + a2[1] + a3[1] + a4[1]) / 4);
            b = Math.round((a1[2] + a2[2] + a3[2] + a4[2]) / 4);

            setPixel(offScreenCanvas, i, j, r, g, b, 255);
        }
    }

    // copy buffer onto the canvas
    c.putImageData(offScreenCanvas, 0, 0);
}

init();
setInterval(animation, 20);

The error is in setPixel definition.

  imageData.data[index]     = r;

Upvotes: 0

Views: 354

Answers (1)

Scott Mermelstein
Scott Mermelstein

Reputation: 15397

First, make friends with your debugger. It'll help you a lot. I find the one on Chrome (press Ctrl-Shift-I) is easiest to work with, but your mileage may vary.

Second, posting your html with the canvas width and height would have been helpful. From your code, I'm deducing that it was apparently 800x100.

So my thought was that the imageData variable passed into setPixel was somehow undefined, but the debugger showed me otherwise. It's a properly defined context2d. But that does lead to the problem you're experiencing.

You properly pass a variable called ctxOffscreen into setPixel, but in setPixel, you're assuming it's the imageData object. It's not. They're different.

Since you're using two contexts, you'll probably want to simply getImageData of your offscreen context. (You can avoid the two contexts and simply have two imageDatas by instead calling c.createImageData, but I'll go with what you've currently got.)

Right after your var ctxOffscreen line, add a new one:

var imgData = ctxOffscreen.getImageData(0, 0, w, h);

Pass that into setPixel and getPixel instead of (ctxOffscreen), e.g.

setPixel(imgData, y, y, r, g, b, 255);

Check out this FIDDLE. It runs. I guess it may run the way you want.

Upvotes: 1

Related Questions