From 84b92d223e1fb170bca4e85b9d11456725f96711 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Sat, 1 Aug 2015 19:33:55 -0700 Subject: [PATCH] allow a `format` and explicit Buffer to be used Also adds new `flush()` function, and `format` getter. This allows a specific Buffer instance to be the "backing store" for a Canvas instance. This is especially useful when interfacing directly with a Linux framebuffer, as it allows for faster rendering and the `format` can be specified to match what the framebuffer is expecting. For example, the PiTFT screen expects 16-bits per pixel to be written to `/dev/fb1`, so we can specify that the CAIRO_FORMAT_RGB16_565 format be used for the Canvas instance. Overall this API could be bikeshedded a bit perhaps, but it's functional at least. Here's a couple videos from my Raspberry Pi: - https://cloudup.com/iz--PO83YR2 - https://cloudup.com/iJmL2M-mdw3 --- src/Canvas.cc | 68 ++++++++++++++++++++++++++++++++++++++++++++------- src/Canvas.h | 5 +++- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/Canvas.cc b/src/Canvas.cc index 7e7701383..62794c56c 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -39,11 +39,13 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { // Prototype Local proto = ctor->PrototypeTemplate(); Nan::SetPrototypeMethod(ctor, "toBuffer", ToBuffer); + Nan::SetPrototypeMethod(ctor, "flush", Flush); Nan::SetPrototypeMethod(ctor, "streamPNGSync", StreamPNGSync); #ifdef HAVE_JPEG Nan::SetPrototypeMethod(ctor, "streamJPEGSync", StreamJPEGSync); #endif Nan::SetAccessor(proto, Nan::New("type").ToLocalChecked(), GetType); + Nan::SetAccessor(proto, Nan::New("format").ToLocalChecked(), GetFormat); Nan::SetAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth); Nan::SetAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight); @@ -55,6 +57,14 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { Nan::SetTemplate(proto, "PNG_FILTER_PAETH", Nan::New(PNG_FILTER_PAETH)); Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New(PNG_ALL_FILTERS)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_INVALID", Nan::New(CAIRO_FORMAT_INVALID)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_ARGB32", Nan::New(CAIRO_FORMAT_ARGB32)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_RGB24", Nan::New(CAIRO_FORMAT_RGB24)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_A8", Nan::New(CAIRO_FORMAT_A8)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_A1", Nan::New(CAIRO_FORMAT_A1)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_RGB16_565", Nan::New(CAIRO_FORMAT_RGB16_565)); + Nan::SetTemplate(proto, "CAIRO_FORMAT_RGB30", Nan::New(CAIRO_FORMAT_RGB30)); + Nan::Set(target, Nan::New("Canvas").ToLocalChecked(), ctor->GetFunction()); } @@ -64,15 +74,26 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { NAN_METHOD(Canvas::New) { int width = 0, height = 0; + unsigned char* data = nullptr; canvas_type_t type = CANVAS_TYPE_IMAGE; + cairo_format_t format = CAIRO_FORMAT_ARGB32; if (info[0]->IsNumber()) width = info[0]->Uint32Value(); if (info[1]->IsNumber()) height = info[1]->Uint32Value(); - if (info[2]->IsString()) type = !strcmp("pdf", *String::Utf8Value(info[2])) - ? CANVAS_TYPE_PDF - : !strcmp("svg", *String::Utf8Value(info[2])) - ? CANVAS_TYPE_SVG - : CANVAS_TYPE_IMAGE; - Canvas *canvas = new Canvas(width, height, type); + if (info[2]->IsString()) { + type = !strcmp("pdf", *String::Utf8Value(info[2])) + ? CANVAS_TYPE_PDF + : !strcmp("svg", *String::Utf8Value(info[2])) + ? CANVAS_TYPE_SVG + : CANVAS_TYPE_IMAGE; + } else if (node::Buffer::HasInstance(info[2])) { + type = CANVAS_TYPE_IMAGE; + data = reinterpret_cast(node::Buffer::Data(info[2])); + } + + if (info[3]->IsNumber()) + format = static_cast(info[3]->Uint32Value()); + + Canvas *canvas = new Canvas(width, height, type, data, format); canvas->Wrap(info.This()); info.GetReturnValue().Set(info.This()); } @@ -86,6 +107,15 @@ NAN_GETTER(Canvas::GetType) { info.GetReturnValue().Set(Nan::New(canvas->isPDF() ? "pdf" : canvas->isSVG() ? "svg" : "image").ToLocalChecked()); } +/* + * Get format string. + */ + +NAN_GETTER(Canvas::GetFormat) { + Canvas *canvas = Nan::ObjectWrap::Unwrap(info.This()); + info.GetReturnValue().Set(Nan::New(canvas->format)); +} + /* * Get width. */ @@ -222,6 +252,21 @@ Canvas::EIO_AfterToBuffer(eio_req *req) { #endif } + +/* + * Non-standard. + * Flushes the context contents to the backing store. + * Useful when an explicit Buffer backing store was passed + * in, and you want the Buffer contents to be up-to-date. + */ + +NAN_METHOD(Canvas::Flush) { + Nan::HandleScope scope; + Canvas *canvas = ObjectWrap::Unwrap(info.This()); + cairo_surface_flush(canvas->surface()); +} + + /* * Convert PNG data to a node::Buffer, async when a * callback function is passed. @@ -466,10 +511,11 @@ NAN_METHOD(Canvas::StreamJPEGSync) { * Initialize cairo surface. */ -Canvas::Canvas(int w, int h, canvas_type_t t): Nan::ObjectWrap() { +Canvas::Canvas(int w, int h, canvas_type_t t, unsigned char* data, cairo_format_t f): Nan::ObjectWrap() { type = t; width = w; height = h; + format = f; _surface = NULL; _closure = NULL; @@ -485,8 +531,12 @@ Canvas::Canvas(int w, int h, canvas_type_t t): Nan::ObjectWrap() { cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS); assert(status == CAIRO_STATUS_SUCCESS); _surface = cairo_svg_surface_create_for_stream(toBuffer, _closure, w, h); + } else if (data) { + _surface = cairo_image_surface_create_for_data(data, format, w, h, + cairo_format_stride_for_width(format, w)); + assert(_surface); } else { - _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + _surface = cairo_image_surface_create(format, w, h); assert(_surface); Nan::AdjustExternalMemory(4 * w * h); } @@ -546,7 +596,7 @@ Canvas::resurface(Local canvas) { int old_width = cairo_image_surface_get_width(_surface); int old_height = cairo_image_surface_get_height(_surface); cairo_surface_destroy(_surface); - _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + _surface = cairo_image_surface_create(format, width, height); Nan::AdjustExternalMemory(4 * (width * height - old_width * old_height)); // Reset context diff --git a/src/Canvas.h b/src/Canvas.h index bf58b9b59..d3a65f541 100644 --- a/src/Canvas.h +++ b/src/Canvas.h @@ -52,10 +52,13 @@ class Canvas: public Nan::ObjectWrap { int width; int height; canvas_type_t type; + cairo_format_t format; static Nan::Persistent constructor; static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); static NAN_METHOD(New); + static NAN_METHOD(Flush); static NAN_METHOD(ToBuffer); + static NAN_GETTER(GetFormat); static NAN_GETTER(GetType); static NAN_GETTER(GetWidth); static NAN_GETTER(GetHeight); @@ -84,7 +87,7 @@ class Canvas: public Nan::ObjectWrap { inline void *closure(){ return _closure; } inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); } inline int stride(){ return cairo_image_surface_get_stride(_surface); } - Canvas(int width, int height, canvas_type_t type); + Canvas(int width, int height, canvas_type_t type, unsigned char* data, cairo_format_t format); void resurface(Local canvas); private: