|
|
@ -18,7 +18,12 @@ using namespace node; |
|
|
|
*/ |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
Persistent<Function> pfn; |
|
|
|
Handle<Function> fn; |
|
|
|
unsigned len; |
|
|
|
uint8_t *data; |
|
|
|
Canvas *canvas; |
|
|
|
cairo_status_t status; |
|
|
|
} closure_t; |
|
|
|
|
|
|
|
/*
|
|
|
@ -33,6 +38,7 @@ Canvas::Initialize(Handle<Object> target) { |
|
|
|
t->SetClassName(String::NewSymbol("Canvas")); |
|
|
|
|
|
|
|
Local<ObjectTemplate> proto = t->PrototypeTemplate(); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "toBuffer", ToBuffer); |
|
|
|
NODE_SET_PROTOTYPE_METHOD(t, "streamPNGSync", StreamPNGSync); |
|
|
|
proto->SetAccessor(String::NewSymbol("width"), GetWidth, SetWidth); |
|
|
|
proto->SetAccessor(String::NewSymbol("height"), GetHeight, SetHeight); |
|
|
@ -102,12 +108,113 @@ Canvas::SetHeight(Local<String> prop, Local<Value> val, const AccessorInfo &info |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Canvas::ToBuffer callback. |
|
|
|
*/ |
|
|
|
|
|
|
|
static cairo_status_t |
|
|
|
toBuffer(void *c, const uint8_t *data, unsigned len) { |
|
|
|
closure_t *closure = (closure_t *) c; |
|
|
|
// TODO: mem handling
|
|
|
|
if (closure->len) { |
|
|
|
closure->data = (uint8_t *) realloc(closure->data, closure->len + len); |
|
|
|
memcpy(closure->data + closure->len, data, len); |
|
|
|
closure->len += len; |
|
|
|
} else { |
|
|
|
closure->data = (uint8_t *) malloc(len); |
|
|
|
memcpy(closure->data, data, len); |
|
|
|
closure->len += len; |
|
|
|
} |
|
|
|
return CAIRO_STATUS_SUCCESS; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* EIO toBuffer callback. |
|
|
|
*/ |
|
|
|
|
|
|
|
static int |
|
|
|
EIO_ToBuffer(eio_req *req) { |
|
|
|
closure_t *closure = (closure_t *) req->data; |
|
|
|
|
|
|
|
closure->status = cairo_surface_write_to_png_stream( |
|
|
|
closure->canvas->getSurface() |
|
|
|
, toBuffer |
|
|
|
, closure); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* EIO after toBuffer callback. |
|
|
|
*/ |
|
|
|
|
|
|
|
static int |
|
|
|
EIO_AfterToBuffer(eio_req *req) { |
|
|
|
HandleScope scope; |
|
|
|
closure_t *closure = (closure_t *) req->data; |
|
|
|
ev_unref(EV_DEFAULT_UC); |
|
|
|
|
|
|
|
if (closure->status) { |
|
|
|
Handle<Value> argv[1] = { Canvas::Error(closure->status) }; |
|
|
|
closure->pfn->Call(Context::GetCurrent()->Global(), 1, argv); |
|
|
|
} else { |
|
|
|
Buffer *buf = Buffer::New(closure->len); |
|
|
|
memcpy(buf->data(), closure->data, closure->len); |
|
|
|
Handle<Value> argv[2] = { Null(), buf->handle_ }; |
|
|
|
closure->pfn->Call(Context::GetCurrent()->Global(), 2, argv); |
|
|
|
} |
|
|
|
|
|
|
|
closure->pfn.Dispose(); |
|
|
|
delete closure; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert PNG data to a node::Buffer, async when a |
|
|
|
* callback function is passed. |
|
|
|
*/ |
|
|
|
|
|
|
|
Handle<Value> |
|
|
|
Canvas::ToBuffer(const Arguments &args) { |
|
|
|
HandleScope scope; |
|
|
|
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This()); |
|
|
|
|
|
|
|
// Async
|
|
|
|
if (args[0]->IsFunction()) { |
|
|
|
closure_t *closure = new closure_t; |
|
|
|
closure->len = 0; |
|
|
|
closure->canvas = canvas; |
|
|
|
// TODO: only one callback fn in closure
|
|
|
|
canvas->Ref(); |
|
|
|
closure->pfn = Persistent<Function>::New(Handle<Function>::Cast(args[0])); |
|
|
|
eio_custom(EIO_ToBuffer, EIO_PRI_DEFAULT, EIO_AfterToBuffer, closure); |
|
|
|
ev_ref(EV_DEFAULT_UC); |
|
|
|
return Undefined(); |
|
|
|
} else { |
|
|
|
closure_t closure; |
|
|
|
closure.len = 0; |
|
|
|
|
|
|
|
TryCatch try_catch; |
|
|
|
cairo_status_t status = cairo_surface_write_to_png_stream(canvas->getSurface(), toBuffer, &closure); |
|
|
|
|
|
|
|
if (try_catch.HasCaught()) { |
|
|
|
return try_catch.ReThrow(); |
|
|
|
} else if (status) { |
|
|
|
return ThrowException(Canvas::Error(status)); |
|
|
|
} else { |
|
|
|
Buffer *buf = Buffer::New(closure.len); |
|
|
|
memcpy(buf->data(), closure.data, closure.len); |
|
|
|
return buf->handle_; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
* Canvas::StreamPNG callback. |
|
|
|
*/ |
|
|
|
|
|
|
|
static cairo_status_t |
|
|
|
writeToBuffer(void *c, const uint8_t *data, unsigned len) { |
|
|
|
streamPNG(void *c, const uint8_t *data, unsigned len) { |
|
|
|
closure_t *closure = (closure_t *) c; |
|
|
|
Buffer *buf = Buffer::New(len); |
|
|
|
#if NODE_VERSION_AT_LEAST(0,3,0) |
|
|
@ -136,7 +243,7 @@ Canvas::StreamPNGSync(const Arguments &args) { |
|
|
|
closure.fn = Handle<Function>::Cast(args[0]); |
|
|
|
|
|
|
|
TryCatch try_catch; |
|
|
|
cairo_status_t status = cairo_surface_write_to_png_stream(canvas->getSurface(), writeToBuffer, &closure); |
|
|
|
cairo_status_t status = cairo_surface_write_to_png_stream(canvas->getSurface(), streamPNG, &closure); |
|
|
|
|
|
|
|
if (try_catch.HasCaught()) { |
|
|
|
return try_catch.ReThrow(); |
|
|
|