6

I'm writing a node-webkit addon in C++ that decodes image data and returns an array of pixel data to the caller. The following code (abridged for brevity) works just fine:

caller.js

var image_data = canvas_context.createImageData(w, h);
var addon = require('decoder.node');
var decoder = new addon.Decoder();
var pixel_array = decoder.getPixelArray();
image_data.data.set(pixel_array);
canvas_context.putImageData(image_data);

decoder.cpp

Handle<Value> Decoder::getPixelArray(const Arguments &args) {
    HandleScope scope;

    // unwrap self
    DecoderObj *obj = ObjectWrap::Unwrap<DecoderObj>(args.This());
    if (obj == NULL) return scope.Close(Number::New(1));

    // get pointer to pixel data
    PixelPtr *ptr = NULL;
    uint64_t len = 0;
    obj->GetPixelsPtr((PixelPtr*)&ptr, &len);

    // create javascript native array
    // from http://luismreis.github.io/node-bindings-guide/docs/returning.html
    v8::Handle<Value> fun_val = Context::GetCurrent()->Global()->Get(String::New("Uint8ClampedArray"));
    v8::Handle<Function> fun = v8::Handle<Function>::Cast(fun_val);
    const unsigned argc = 1;
    Local<Value> argv[argc] = { Local<Value>::New(Uint32::New(len)) };
    Local<v8::Object> array = fun->NewInstance(argc, argv);

    // populate js native array with pixel ptr data
    array->SetIndexedPropertiesToExternalArrayData(ptr, v8::kExternalUnsignedByteArray, len);

    // return native array to javascript caller
    return scope.Close(array);
}

The only problem is when the caller invokes the set() method on the ImageData object. This has proven to be unacceptably slow (i.e. >= 20ms). I assume the slowness is due to a large amount of data being copied from one memory location to another. So I thought I'd try to fix this by passing a reference to the ImageData object into the addon, and then have the addon write directly to the object, thereby eliminating the need for a memory copy. Unfortunately I haven't been able to get this to work. Here's the modified code that doesn't work:

caller.js

var image_data = canvas_context.createImageData(w, h);
var addon = require('decoder.node');
var decoder = new addon.Decoder();
decoder.getPixelArray(image_data.data);
canvas_context.putImageData(image_data);

decoder.cpp

Handle<Value> Decoder::getPixelArray(const Arguments &args) {
    HandleScope scope;

    // unwrap self
    DecoderObj *obj = ObjectWrap::Unwrap<DecoderObj>(args.This());
    if (obj == NULL) return scope.Close(Number::New(1));

    // convert image_data arg into local array
    Local<Array> array = Array::Cast(*args[0]);

    // get pointer to pixel data
    PixelPtr *ptr = NULL;
    uint64_t len = 0;
    obj->GetPixelsPtr((PixelPtr*)&ptr, &len);

    // populate image_data array with pixel ptr data
    array->SetIndexedPropertiesToExternalArrayData(ptr, v8::kExternalUnsignedByteArray, len);
}

I know this doesn't work because putImageData() results in a black frame, whereas the original code successfully draws the actual frame as expected.

Here is my question: is it possible to create an ImageData object and/or Uint8ClampedArray in JavaScript, pass it by reference into a Node.js addon, have the addon write to the object's memory location, and then be able to use the object in the JavaScript context without performing a memory copy??

I don't know if I'm doing it wrong, or it's impossible, or both. :)

  • Aren't node.js addon and webkit different V8 instances? – Petr Feb 01 '15 at 23:11
  • @Petr This is a node-webkit addon not node.js. Sorry for the confusion. I edited the post. – David Dudas Feb 02 '15 at 06:55
  • are you sure that args[0] can be casted to a plain `Array`? I would probably try to do it the opposite way - allocate the image, call image decoder with the pointer to its data. This will eliminate the call to `.set()`, but I'm not really sure if this will work or not. Btw I'm working on an addon that will bring an asynchronous imaging for node.js (http://blend2d.com), but it's not ready yet. – Petr Mar 03 '15 at 21:25
  • I think this should work for you http://stackoverflow.com/a/42143138/174527 – Jayesh Feb 09 '17 at 17:28

0 Answers0