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?