0
votes

I have a FireBreath plugin that paints to a 800 x 480 window at 10 Hz. I would like to duplicate this window on the HTML page as a thumbnail view (something like 120 x 72). Ideally the thumbnail would update at 10 Hz too, although 1 Hz would be acceptable.

I have some working code, but it's too slow to be useable.

The Javascript starts out getting the canvas context and an array to manipulate (inspired by https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/):

var canvas = document.getElementById('thumbnail')
var context = canvas.getContext('2d');
var imgData = context.createImageData(120, 72);
var buf = new ArrayBuffer(imgData.data.length);
var buf8 = new Uint8ClampedArray(buf);
var data = new Uint32Array(buf);

In JavaScript I would do something like

for (n = 0; n < data.length; ++n) {
    data[n] = 0xff0000ff; // paint pixel red
}

followed by

imgData.data.set(buf8);
context.putImageData(imgData, 0, 0);

which executes very quickly. Trouble is, I want to pass responsibility for drawing the thumbnail over to the plugin. So instead of the JavaScript loop I do something like document.getElementById("plugin").paintThumbnail(data), passing in the Uint32Array data as a parameter.

The implementation of paintThumbnail in the plugin looks like this:

void MyPluginAPI::paintThumbnail(const FB::JSObjectPtr& data) {
    if (!data)
        throw FB::invalid_arguments();
    int size = data->GetProperty("length").convert_cast<int>();

    /* method 1 - call "set" per pixel - slowest */
    for (int n = 0; n < size; n++) {
        FB::VariantList pixel = FB::variant_list_of(0xff0000ff);
        data->Invoke("set", FB::variant_list_of(pixel)(n)); // paint the nth pixel red
    }
    /* -- */

    /* method 2 - call "SetProperty" per pixel - faster */
    for (int n = 0; n < size; n++) {
        data->SetProperty(n, 0xff0000ff); // paint the nth pixel red
    }
    /* -- */

    /* method 3 - write to array buffer then call "set" once - fastest */
    uint32_t* vals = new uint32_t[size];
    for (int n = 0; n < size; n++) {
        vals[n] = 0xff0000ff; // paint the nth pixel red
    }

    std::vector<uint32_t> valVec(vals, vals + size);
    FB::VariantList vars = FB::make_variant_list(valVec);
    data->Invoke("set", FB::variant_list_of(vars));
    /* -- */
}

(Obviously, once I get this working I will paint something interesting - not just red.) Each of the three methods above for modifying the JSObjectPtr data are much slower than the direct JavaScript, and no where near achieving a 10 Hz frame rate (testing was done in Chrome).

How could I make the plugin code faster? Is there another way to achieve the same result? Or is this just a limitation of plugins?

1

1 Answers

0
votes

The only semi-performant way to transfer binary data to javascript from a NPAPI plugin of any kind is to base64 encode it and send it as a string.

If I were doing what you're trying to I'd probably encode it as a jpeg or something and send it back to use in an img tag as a data uri.

If you try to send it pixel by pixel your performance won't be anywhere close to fast enough.