diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f155f0d..d28663661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ================== ### Changed ### Added +* support nodejs worker ### Fixed * Stringify CanvasGradient, CanvasPattern and ImageData like browsers do. (#1639, #1646) * Add missing include for `toupper`. diff --git a/binding.gyp b/binding.gyp index 57f14ab8c..ac125d621 100644 --- a/binding.gyp +++ b/binding.gyp @@ -59,6 +59,8 @@ 'target_name': 'canvas', 'include_dirs': ["(ins); + data->canvas_ctor_tpl.Reset(); + data->context2d_ctor_tpl.Reset(); + data->context2d_dom_matrix.Reset(); + data->context2d_parse_font.Reset(); + data->gradient_ctor_tpl.Reset(); + data->image_ctor_tpl.Reset(); + data->image_data_ctor_tpl.Reset(); + data->image_backend_ctor_tpl.Reset(); + data->pdf_backend_ctor_tpl.Reset(); + data->svg_backend_ctor_tpl.Reset(); + data->pattern_ctor_tpl.Reset(); + data->pattern_dom_matrix.Reset(); + + delete data; +} \ No newline at end of file diff --git a/src/AddonData.h b/src/AddonData.h new file mode 100644 index 000000000..6f5876281 --- /dev/null +++ b/src/AddonData.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +/* + * FontFace describes a font file in terms of one PangoFontDescription that + * will resolve to it and one that the user describes it as (like @font-face) + */ +class FontFace { + public: + PangoFontDescription *sys_desc = nullptr; + PangoFontDescription *user_desc = nullptr; + unsigned char file_path[1024]; +}; + +class AddonData { + public: + Nan::Persistent canvas_ctor_tpl; + Nan::Persistent gradient_ctor_tpl; + Nan::Persistent context2d_ctor_tpl; + Nan::Persistent image_data_ctor_tpl; + Nan::Persistent image_ctor_tpl; + Nan::Persistent pattern_ctor_tpl; + Nan::Persistent image_backend_ctor_tpl; + Nan::Persistent pdf_backend_ctor_tpl; + Nan::Persistent svg_backend_ctor_tpl; + Nan::Persistent context2d_dom_matrix; + Nan::Persistent context2d_parse_font; + Nan::Persistent pattern_dom_matrix; + + AddonData(); + + static void Dispose(void*); +}; diff --git a/src/Backends.cc b/src/Backends.cc index 2256c32b6..903ea6fd5 100644 --- a/src/Backends.cc +++ b/src/Backends.cc @@ -6,13 +6,13 @@ using namespace v8; -void Backends::Initialize(Local target) { +void Backends::Initialize(Local target, AddonData* data) { Nan::HandleScope scope; Local obj = Nan::New(); - ImageBackend::Initialize(obj); - PdfBackend::Initialize(obj); - SvgBackend::Initialize(obj); + ImageBackend::Initialize(obj, data); + PdfBackend::Initialize(obj, data); + SvgBackend::Initialize(obj, data); Nan::Set(target, Nan::New("Backends").ToLocalChecked(), obj).Check(); } diff --git a/src/Backends.h b/src/Backends.h index dbea053ce..056b17405 100644 --- a/src/Backends.h +++ b/src/Backends.h @@ -3,8 +3,9 @@ #include "backend/Backend.h" #include #include +#include "AddonData.h" class Backends : public Nan::ObjectWrap { public: - static void Initialize(v8::Local target); + static void Initialize(v8::Local target, AddonData*); }; diff --git a/src/Canvas.cc b/src/Canvas.cc index bd39d4329..93e538125 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -21,6 +21,7 @@ #include "Util.h" #include #include "node_buffer.h" +#include "lock.h" #ifdef HAVE_JPEG #include "JPEGStream.h" @@ -38,8 +39,10 @@ using namespace v8; using namespace std; -Nan::Persistent Canvas::constructor; +const char *Canvas::ctor_name = "Canvas"; +// these variables are protected by uv_rwlock; +UVLockHandle rwlock_handle; std::vector font_face_list; /* @@ -47,14 +50,15 @@ std::vector font_face_list; */ void -Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData* addon_data) { Nan::HandleScope scope; + Local data_holder = Nan::New(addon_data); // Constructor - Local ctor = Nan::New(Canvas::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("Canvas").ToLocalChecked()); + Local ctor = Nan::New(Canvas::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); + addon_data->canvas_ctor_tpl.Reset(ctor); // Prototype Local proto = ctor->PrototypeTemplate(); @@ -78,12 +82,12 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New(PNG_ALL_FILTERS)); // Class methods - Nan::SetMethod(ctor, "_registerFont", RegisterFont); - Nan::SetMethod(ctor, "_deregisterAllFonts", DeregisterAllFonts); + Nan::SetMethod(ctor, "_registerFont", RegisterFont, data_holder); + Nan::SetMethod(ctor, "_deregisterAllFonts", DeregisterAllFonts, data_holder); Local ctx = Nan::GetCurrentContext(); Nan::Set(target, - Nan::New("Canvas").ToLocalChecked(), + Nan::New(ctor_name).ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); } @@ -96,6 +100,7 @@ NAN_METHOD(Canvas::New) { return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'"); } + AddonData *addon_data = reinterpret_cast(info.Data().As()->Value()); Backend* backend = NULL; if (info[0]->IsNumber()) { int width = Nan::To(info[0]).FromMaybe(0), height = 0; @@ -114,9 +119,12 @@ NAN_METHOD(Canvas::New) { backend = new ImageBackend(width, height); } else if (info[0]->IsObject()) { - if (Nan::New(ImageBackend::constructor)->HasInstance(info[0]) || - Nan::New(PdfBackend::constructor)->HasInstance(info[0]) || - Nan::New(SvgBackend::constructor)->HasInstance(info[0])) { + Local image_backend = Nan::New(addon_data->image_backend_ctor_tpl); + Local pdf_backend = Nan::New(addon_data->pdf_backend_ctor_tpl); + Local svg_backend = Nan::New(addon_data->svg_backend_ctor_tpl); + if (image_backend->HasInstance(info[0]) || + pdf_backend->HasInstance(info[0]) || + svg_backend->HasInstance(info[0])) { backend = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); }else{ return Nan::ThrowTypeError("Invalid arguments"); @@ -133,6 +141,7 @@ NAN_METHOD(Canvas::New) { Canvas* canvas = new Canvas(backend); canvas->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); backend->setCanvas(canvas); @@ -743,6 +752,8 @@ NAN_METHOD(Canvas::RegisterFont) { pango_font_description_set_style(user_desc, Canvas::GetStyleFromCSSString(style)); pango_font_description_set_family(user_desc, family); + UVLocker locker(rwlock_handle, UVLocker::LockerType::mutex); + auto found = std::find_if(font_face_list.begin(), font_face_list.end(), [&](FontFace& f) { return pango_font_description_equal(f.sys_desc, sys_desc); }); @@ -760,6 +771,8 @@ NAN_METHOD(Canvas::RegisterFont) { pango_font_description_free(user_desc); Nan::ThrowError("Could not load font to the system's font host"); } + + locker.clean(); } else { pango_font_description_free(user_desc); Nan::ThrowError(GENERIC_FACE_ERROR); @@ -773,14 +786,21 @@ NAN_METHOD(Canvas::RegisterFont) { NAN_METHOD(Canvas::DeregisterAllFonts) { // Unload all fonts from pango to free up memory bool success = true; + + UVLocker locker(rwlock_handle, UVLocker::LockerType::rd); + auto begin = font_face_list.begin(); + auto end = font_face_list.end(); + locker.clean(); - std::for_each(font_face_list.begin(), font_face_list.end(), [&](FontFace& f) { + std::for_each(begin, end, [&](FontFace& f) { if (!deregister_font( (unsigned char *)f.file_path )) success = false; pango_font_description_free(f.user_desc); pango_font_description_free(f.sys_desc); }); - + + locker = UVLocker(rwlock_handle, UVLocker::LockerType::wr); font_face_list.clear(); + locker.clean(); if (!success) Nan::ThrowError("Could not deregister one or more fonts"); } @@ -862,7 +882,7 @@ Canvas::GetWeightFromCSSString(const char *weight) { */ PangoFontDescription * -Canvas::ResolveFontDescription(const PangoFontDescription *desc) { +Canvas::ResolveFontDescription(const PangoFontDescription *desc, AddonData *addon_data) { // One of the user-specified families could map to multiple SFNT family names // if someone registered two different fonts under the same family name. // https://drafts.csswg.org/css-fonts-3/#font-style-matching @@ -874,6 +894,7 @@ Canvas::ResolveFontDescription(const PangoFontDescription *desc) { for (string family; getline(families, family, ','); ) { string renamed_families; + UVLocker locker(rwlock_handle, UVLocker::LockerType::rd); for (auto& ff : font_face_list) { string pangofamily = string(pango_font_description_get_family(ff.user_desc)); if (streq_casein(family, pangofamily)) { @@ -893,6 +914,7 @@ Canvas::ResolveFontDescription(const PangoFontDescription *desc) { } } } + locker.clean(); if (resolved_families.size()) resolved_families += ','; resolved_families += renamed_families.size() ? renamed_families : family; diff --git a/src/Canvas.h b/src/Canvas.h index f356af035..2858d5066 100644 --- a/src/Canvas.h +++ b/src/Canvas.h @@ -10,6 +10,7 @@ #include #include #include +#include "AddonData.h" /* * Maxmimum states per context. @@ -20,16 +21,6 @@ #define CANVAS_MAX_STATES 64 #endif -/* - * FontFace describes a font file in terms of one PangoFontDescription that - * will resolve to it and one that the user describes it as (like @font-face) - */ -class FontFace { - public: - PangoFontDescription *sys_desc = nullptr; - PangoFontDescription *user_desc = nullptr; - unsigned char file_path[1024]; -}; /* * Canvas. @@ -37,8 +28,8 @@ class FontFace { class Canvas: public Nan::ObjectWrap { public: - static Nan::Persistent constructor; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static const char *ctor_name; + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_METHOD(ToBuffer); static NAN_GETTER(GetType); @@ -58,7 +49,7 @@ class Canvas: public Nan::ObjectWrap { static void ToBufferAsyncAfter(uv_work_t *req); static PangoWeight GetWeightFromCSSString(const char *weight); static PangoStyle GetStyleFromCSSString(const char *style); - static PangoFontDescription *ResolveFontDescription(const PangoFontDescription *desc); + static PangoFontDescription *ResolveFontDescription(const PangoFontDescription *desc, AddonData*); DLL_PUBLIC inline Backend* backend() { return _backend; } DLL_PUBLIC inline cairo_surface_t* surface(){ return backend()->getSurface(); } diff --git a/src/CanvasGradient.cc b/src/CanvasGradient.cc index 280fc2e8c..0a32254d2 100644 --- a/src/CanvasGradient.cc +++ b/src/CanvasGradient.cc @@ -7,27 +7,28 @@ using namespace v8; -Nan::Persistent Gradient::constructor; +const char *Gradient::ctor_name = "CanvasGradient"; /* * Initialize CanvasGradient. */ void -Gradient::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +Gradient::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData *addon_data) { Nan::HandleScope scope; + Local data_holder = Nan::New(addon_data); // Constructor - Local ctor = Nan::New(Gradient::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("CanvasGradient").ToLocalChecked()); + Local ctor = Nan::New(Gradient::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); + addon_data->gradient_ctor_tpl.Reset(ctor); // Prototype Nan::SetPrototypeMethod(ctor, "addColorStop", AddColorStop); Local ctx = Nan::GetCurrentContext(); Nan::Set(target, - Nan::New("CanvasGradient").ToLocalChecked(), + Nan::New(ctor_name).ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); } @@ -40,6 +41,7 @@ NAN_METHOD(Gradient::New) { return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'"); } + info.This()->SetInternalField(1, info.Data()); // Linear if (4 == info.Length()) { Gradient *grad = new Gradient( diff --git a/src/CanvasGradient.h b/src/CanvasGradient.h index b6902c428..ae1f2a722 100644 --- a/src/CanvasGradient.h +++ b/src/CanvasGradient.h @@ -5,11 +5,13 @@ #include #include #include +#include +#include "AddonData.h" class Gradient: public Nan::ObjectWrap { public: - static Nan::Persistent constructor; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static const char *ctor_name; + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_METHOD(AddColorStop); Gradient(double x0, double y0, double x1, double y1); diff --git a/src/CanvasPattern.cc b/src/CanvasPattern.cc index fa3848b37..5a91d7140 100644 --- a/src/CanvasPattern.cc +++ b/src/CanvasPattern.cc @@ -2,35 +2,36 @@ #include "CanvasPattern.h" +#include "Util.h" #include "Canvas.h" #include "Image.h" using namespace v8; const cairo_user_data_key_t *pattern_repeat_key; - -Nan::Persistent Pattern::constructor; -Nan::Persistent Pattern::_DOMMatrix; +const char *Pattern::ctor_name = "CanvasPattern"; +const char *Pattern::dom_matrix_name = "CanvasPattern_DOMMatrix"; /* * Initialize CanvasPattern. */ void -Pattern::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +Pattern::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData *addon_data) { Nan::HandleScope scope; + Local data_holder = Nan::New(addon_data); // Constructor - Local ctor = Nan::New(Pattern::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("CanvasPattern").ToLocalChecked()); + Local ctor = Nan::New(Pattern::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); + addon_data->pattern_ctor_tpl.Reset(ctor); Nan::SetPrototypeMethod(ctor, "setTransform", SetTransform); // Prototype Local ctx = Nan::GetCurrentContext(); - Nan::Set(target, Nan::New("CanvasPattern").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); - Nan::Set(target, Nan::New("CanvasPatternInit").ToLocalChecked(), Nan::New(SaveExternalModules)); + Nan::Set(target, Nan::New(ctor_name).ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); + Nan::Set(target, Nan::New("CanvasPatternInit").ToLocalChecked(), Nan::New(SaveExternalModules, data_holder)); } /* @@ -38,7 +39,9 @@ Pattern::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { */ NAN_METHOD(Pattern::SaveExternalModules) { - _DOMMatrix.Reset(Nan::To(info[0]).ToLocalChecked()); + AddonData *addon_data = reinterpret_cast(info.Data().As()->Value()); + + addon_data->pattern_dom_matrix.Reset(Nan::To(info[0]).ToLocalChecked()); } /* @@ -51,11 +54,14 @@ NAN_METHOD(Pattern::New) { } cairo_surface_t *surface; + AddonData *addon_data = reinterpret_cast(info.Data().As()->Value()); Local obj = Nan::To(info[0]).ToLocalChecked(); + Local canvas_ctor_tpl = Nan::New(addon_data->canvas_ctor_tpl); + Local image_ctor_tpl = Nan::New(addon_data->image_ctor_tpl); // Image - if (Nan::New(Image::constructor)->HasInstance(obj)) { + if (image_ctor_tpl->HasInstance(obj)) { Image *img = Nan::ObjectWrap::Unwrap(obj); if (!img->isComplete()) { return Nan::ThrowError("Image given has not completed loading"); @@ -63,7 +69,7 @@ NAN_METHOD(Pattern::New) { surface = img->surface(); // Canvas - } else if (Nan::New(Canvas::constructor)->HasInstance(obj)) { + } else if (canvas_ctor_tpl->HasInstance(obj)) { Canvas *canvas = Nan::ObjectWrap::Unwrap(obj); surface = canvas->surface(); // Invalid @@ -80,6 +86,7 @@ NAN_METHOD(Pattern::New) { } Pattern *pattern = new Pattern(surface, repeat); pattern->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); info.GetReturnValue().Set(info.This()); } @@ -87,12 +94,14 @@ NAN_METHOD(Pattern::New) { * Set the pattern-space to user-space transform. */ NAN_METHOD(Pattern::SetTransform) { + AddonData *addon_data = get_data_from_if1(info.This()); Pattern *pattern = Nan::ObjectWrap::Unwrap(info.This()); Local ctx = Nan::GetCurrentContext(); Local mat = Nan::To(info[0]).ToLocalChecked(); #if NODE_MAJOR_VERSION >= 8 - if (!mat->InstanceOf(ctx, _DOMMatrix.Get(Isolate::GetCurrent())).ToChecked()) { + Local _DOMMatrix = Nan::New(addon_data->context2d_dom_matrix); + if (!mat->InstanceOf(ctx, _DOMMatrix).ToChecked()) { return Nan::ThrowTypeError("Expected DOMMatrix"); } #endif diff --git a/src/CanvasPattern.h b/src/CanvasPattern.h index 29e2171b6..8d4f3cb89 100644 --- a/src/CanvasPattern.h +++ b/src/CanvasPattern.h @@ -5,6 +5,7 @@ #include #include #include +#include "AddonData.h" /* * Canvas types. @@ -21,9 +22,9 @@ extern const cairo_user_data_key_t *pattern_repeat_key; class Pattern: public Nan::ObjectWrap { public: - static Nan::Persistent constructor; - static Nan::Persistent _DOMMatrix; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static const char *ctor_name; + static const char *dom_matrix_name; + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_METHOD(SaveExternalModules); static NAN_METHOD(SetTransform); diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index b98fe4520..6e02b14bf 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -21,8 +21,6 @@ using namespace v8; -Nan::Persistent Context2d::constructor; - /* * Rectangle arg assertions. */ @@ -81,22 +79,20 @@ inline static bool checkArgs(const Nan::FunctionCallbackInfo &info, doubl return areArgsValid; } -Nan::Persistent Context2d::_DOMMatrix; -Nan::Persistent Context2d::_parseFont; - /* * Initialize Context2d. */ void -Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData* addon_data) { Nan::HandleScope scope; + Local data_holder = Nan::New(addon_data); // Constructor - Local ctor = Nan::New(Context2d::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); + Local ctor = Nan::New(Context2d::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); ctor->SetClassName(Nan::New("CanvasRenderingContext2D").ToLocalChecked()); + addon_data->context2d_ctor_tpl.Reset(ctor); // Prototype Local proto = ctor->PrototypeTemplate(); @@ -164,7 +160,7 @@ Context2d::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { SetProtoAccessor(proto, Nan::New("textAlign").ToLocalChecked(), GetTextAlign, SetTextAlign, ctor); Local ctx = Nan::GetCurrentContext(); Nan::Set(target, Nan::New("CanvasRenderingContext2d").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); - Nan::Set(target, Nan::New("CanvasRenderingContext2dInit").ToLocalChecked(), Nan::New(SaveExternalModules)); + Nan::Set(target, Nan::New("CanvasRenderingContext2dInit").ToLocalChecked(), Nan::New(SaveExternalModules, data_holder)); } /* @@ -674,8 +670,10 @@ NAN_METHOD(Context2d::New) { if (!info[0]->IsObject()) return Nan::ThrowTypeError("Canvas expected"); + AddonData *addon_data = reinterpret_cast(info.Data().As()->Value()); Local obj = Nan::To(info[0]).ToLocalChecked(); - if (!Nan::New(Canvas::constructor)->HasInstance(obj)) + Local canvas_ctor = Nan::New(addon_data->canvas_ctor_tpl); + if (!canvas_ctor->HasInstance(obj)) return Nan::ThrowTypeError("Canvas expected"); Canvas *canvas = Nan::ObjectWrap::Unwrap(obj); @@ -710,6 +708,7 @@ NAN_METHOD(Context2d::New) { Context2d *context = new Context2d(canvas); context->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); info.GetReturnValue().Set(info.This()); } @@ -718,8 +717,9 @@ NAN_METHOD(Context2d::New) { */ NAN_METHOD(Context2d::SaveExternalModules) { - _DOMMatrix.Reset(Nan::To(info[0]).ToLocalChecked()); - _parseFont.Reset(Nan::To(info[1]).ToLocalChecked()); + AddonData *addon_data = reinterpret_cast(info.Data().As()->Value()); + addon_data->context2d_dom_matrix.Reset(Nan::To(info[0]).ToLocalChecked()); + addon_data->context2d_parse_font.Reset(Nan::To(info[1]).ToLocalChecked()); } /* @@ -773,7 +773,9 @@ NAN_METHOD(Context2d::PutImageData) { if (!info[0]->IsObject()) return Nan::ThrowTypeError("ImageData expected"); Local obj = Nan::To(info[0]).ToLocalChecked(); - if (!Nan::New(ImageData::constructor)->HasInstance(obj)) + AddonData *addon_data = get_data_from_if1(info.This()); + Local image_data_ctor_tpl = Nan::New(addon_data->image_data_ctor_tpl); + if (!image_data_ctor_tpl->HasInstance(obj)) return Nan::ThrowTypeError("ImageData expected"); Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); @@ -1121,7 +1123,8 @@ NAN_METHOD(Context2d::GetImageData) { Local shHandle = Nan::New(sh); Local argv[argc] = { dataArray, swHandle, shHandle }; - Local ctor = Nan::GetFunction(Nan::New(ImageData::constructor)).ToLocalChecked(); + AddonData *addon_data = get_data_from_if1(info.This()); + Local ctor = Nan::GetFunction(Nan::New(addon_data->image_data_ctor_tpl)).ToLocalChecked(); Local instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked(); info.GetReturnValue().Set(instance); @@ -1162,7 +1165,8 @@ NAN_METHOD(Context2d::CreateImageData){ const int argc = 3; Local argv[argc] = { arr, Nan::New(width), Nan::New(height) }; - Local ctor = Nan::GetFunction(Nan::New(ImageData::constructor)).ToLocalChecked(); + AddonData *addon_data = get_data_from_if1(info.This()); + Local ctor = Nan::GetFunction(Nan::New(addon_data->image_data_ctor_tpl)).ToLocalChecked(); Local instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked(); info.GetReturnValue().Set(instance); @@ -1216,10 +1220,13 @@ NAN_METHOD(Context2d::DrawImage) { cairo_surface_t *surface; + AddonData *addon_data = get_data_from_if1(info.This()); Local obj = Nan::To(info[0]).ToLocalChecked(); + Local canvas_ctor_tpl = Nan::New(addon_data->canvas_ctor_tpl); + Local image_ctor_tpl = Nan::New(addon_data->image_ctor_tpl); // Image - if (Nan::New(Image::constructor)->HasInstance(obj)) { + if (image_ctor_tpl->HasInstance(obj)) { Image *img = Nan::ObjectWrap::Unwrap(obj); if (!img->isComplete()) { return Nan::ThrowError("Image given has not completed loading"); @@ -1229,7 +1236,7 @@ NAN_METHOD(Context2d::DrawImage) { surface = img->surface(); // Canvas - } else if (Nan::New(Canvas::constructor)->HasInstance(obj)) { + } else if (canvas_ctor_tpl->HasInstance(obj)) { Canvas *canvas = Nan::ObjectWrap::Unwrap(obj); source_w = sw = canvas->getWidth(); source_h = sh = canvas->getHeight(); @@ -1745,7 +1752,7 @@ NAN_SETTER(Context2d::SetQuality) { */ Local -get_current_transform(Context2d *context) { +get_current_transform(Context2d *context, AddonData *addon_data) { Isolate *iso = Isolate::GetCurrent(); Local arr = Float64Array::New(ArrayBuffer::New(iso, 48), 0, 6); @@ -1761,7 +1768,8 @@ get_current_transform(Context2d *context) { const int argc = 1; Local argv[argc] = { arr }; - return Nan::NewInstance(context->_DOMMatrix.Get(iso), argc, argv).ToLocalChecked(); + Local _DOMMatrix = Nan::New(addon_data->context2d_dom_matrix); + return Nan::NewInstance(_DOMMatrix, argc, argv).ToLocalChecked(); } /* @@ -1785,8 +1793,9 @@ void parse_matrix_from_object(cairo_matrix_t &matrix, Local mat) { */ NAN_GETTER(Context2d::GetCurrentTransform) { + AddonData *addon_data = get_data_from_if1(info.This()); Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); - Local instance = get_current_transform(context); + Local instance = get_current_transform(context, addon_data); info.GetReturnValue().Set(instance); } @@ -1796,12 +1805,14 @@ NAN_GETTER(Context2d::GetCurrentTransform) { */ NAN_SETTER(Context2d::SetCurrentTransform) { + AddonData *addon_data = get_data_from_if1(info.This()); Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); Local ctx = Nan::GetCurrentContext(); Local mat = Nan::To(value).ToLocalChecked(); + Local _DOMMatrix = Nan::New(addon_data->context2d_dom_matrix); #if NODE_MAJOR_VERSION >= 8 - if (!mat->InstanceOf(ctx, _DOMMatrix.Get(Isolate::GetCurrent())).ToChecked()) { + if (!mat->InstanceOf(ctx, _DOMMatrix).ToChecked()) { return Nan::ThrowTypeError("Expected DOMMatrix"); } #endif @@ -1835,6 +1846,7 @@ NAN_GETTER(Context2d::GetFillStyle) { NAN_SETTER(Context2d::SetFillStyle) { Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); + AddonData *addon_data = get_data_from_if1(info.This()); if (value->IsString()) { MaybeLocal mstr = Nan::To(value); @@ -1843,12 +1855,14 @@ NAN_SETTER(Context2d::SetFillStyle) { context->_fillStyle.Reset(); context->_setFillColor(str); } else if (value->IsObject()) { + Local gradient_ctor_tpl = Nan::New(addon_data->gradient_ctor_tpl); + Local pattern_ctor_tpl = Nan::New(addon_data->pattern_ctor_tpl); Local obj = Nan::To(value).ToLocalChecked(); - if (Nan::New(Gradient::constructor)->HasInstance(obj)) { + if (gradient_ctor_tpl->HasInstance(obj)) { context->_fillStyle.Reset(value); Gradient *grad = Nan::ObjectWrap::Unwrap(obj); context->state->fillGradient = grad->pattern(); - } else if (Nan::New(Pattern::constructor)->HasInstance(obj)) { + } else if (pattern_ctor_tpl->HasInstance(obj)) { context->_fillStyle.Reset(value); Pattern *pattern = Nan::ObjectWrap::Unwrap(obj); context->state->fillPattern = pattern->pattern(); @@ -1878,6 +1892,7 @@ NAN_GETTER(Context2d::GetStrokeStyle) { NAN_SETTER(Context2d::SetStrokeStyle) { Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); + AddonData *addon_data = get_data_from_if1(info.This()); if (value->IsString()) { MaybeLocal mstr = Nan::To(value); @@ -1887,11 +1902,13 @@ NAN_SETTER(Context2d::SetStrokeStyle) { context->_setStrokeColor(str); } else if (value->IsObject()) { Local obj = Nan::To(value).ToLocalChecked(); - if (Nan::New(Gradient::constructor)->HasInstance(obj)) { + Local pattern_ctor_tpl = Nan::New(addon_data->pattern_ctor_tpl); + Local gradient_ctor_tpl = Nan::New(addon_data->gradient_ctor_tpl); + if (gradient_ctor_tpl->HasInstance(obj)) { context->_strokeStyle.Reset(value); Gradient *grad = Nan::ObjectWrap::Unwrap(obj); context->state->strokeGradient = grad->pattern(); - } else if (Nan::New(Pattern::constructor)->HasInstance(obj)) { + } else if (pattern_ctor_tpl->HasInstance(obj)) { context->_strokeStyle.Reset(value); Pattern *pattern = Nan::ObjectWrap::Unwrap(obj); context->state->strokePattern = pattern->pattern(); @@ -2094,6 +2111,7 @@ Local Context2d::_getStrokeColor() { } NAN_METHOD(Context2d::CreatePattern) { + AddonData *addon_data = get_data_from_if1(info.This()); Local image = info[0]; Local repetition = info[1]; @@ -2103,7 +2121,7 @@ NAN_METHOD(Context2d::CreatePattern) { const int argc = 2; Local argv[argc] = { image, repetition }; - Local ctor = Nan::GetFunction(Nan::New(Pattern::constructor)).ToLocalChecked(); + Local ctor = Nan::GetFunction(Nan::New(addon_data->pattern_ctor_tpl)).ToLocalChecked(); Local instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked(); info.GetReturnValue().Set(instance); @@ -2111,9 +2129,10 @@ NAN_METHOD(Context2d::CreatePattern) { NAN_METHOD(Context2d::CreateLinearGradient) { const int argc = 4; + AddonData *addon_data = get_data_from_if1(info.This()); Local argv[argc] = { info[0], info[1], info[2], info[3] }; + Local ctor = Nan::GetFunction(Nan::New(addon_data->gradient_ctor_tpl)).ToLocalChecked(); - Local ctor = Nan::GetFunction(Nan::New(Gradient::constructor)).ToLocalChecked(); Local instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked(); info.GetReturnValue().Set(instance); @@ -2121,9 +2140,10 @@ NAN_METHOD(Context2d::CreateLinearGradient) { NAN_METHOD(Context2d::CreateRadialGradient) { const int argc = 6; + AddonData *addon_data = get_data_from_if1(info.This()); Local argv[argc] = { info[0], info[1], info[2], info[3], info[4], info[5] }; - Local ctor = Nan::GetFunction(Nan::New(Gradient::constructor)).ToLocalChecked(); + Local ctor = Nan::GetFunction(Nan::New(addon_data->gradient_ctor_tpl)).ToLocalChecked(); Local instance = Nan::NewInstance(ctor, argc, argv).ToLocalChecked(); info.GetReturnValue().Set(instance); @@ -2256,8 +2276,9 @@ NAN_METHOD(Context2d::Transform) { */ NAN_METHOD(Context2d::GetTransform) { + AddonData *addon_data = get_data_from_if1(info.This()); Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); - Local instance = get_current_transform(context); + Local instance = get_current_transform(context, addon_data); info.GetReturnValue().Set(instance); } @@ -2277,12 +2298,14 @@ NAN_METHOD(Context2d::ResetTransform) { NAN_METHOD(Context2d::SetTransform) { Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); + AddonData *addon_data = get_data_from_if1(info.This()); if (info.Length() == 1) { Local mat = Nan::To(info[0]).ToLocalChecked(); #if NODE_MAJOR_VERSION >= 8 Local ctx = Nan::GetCurrentContext(); - if (!mat->InstanceOf(ctx, _DOMMatrix.Get(Isolate::GetCurrent())).ToChecked()) { + Local _DOMMatrix = Nan::New(addon_data->context2d_dom_matrix); + if (!mat->InstanceOf(ctx, _DOMMatrix).ToChecked()) { return Nan::ThrowTypeError("Expected DOMMatrix"); } #endif @@ -2540,16 +2563,17 @@ NAN_GETTER(Context2d::GetFont) { NAN_SETTER(Context2d::SetFont) { if (!value->IsString()) return; - Isolate *iso = Isolate::GetCurrent(); Local ctx = Nan::GetCurrentContext(); Local str = Nan::To(value).ToLocalChecked(); if (!str->Length()) return; const int argc = 1; + AddonData *addon_data = get_data_from_if1(info.This()); Local argv[argc] = { value }; + Local _parseFont = Nan::New(addon_data->context2d_parse_font); - Local mparsed = Nan::Call(_parseFont.Get(iso), ctx->Global(), argc, argv).ToLocalChecked(); + Local mparsed = Nan::Call(_parseFont, ctx->Global(), argc, argv).ToLocalChecked(); // parseFont returns undefined for invalid CSS font strings if (mparsed->IsUndefined()) return; Local font = Nan::To(mparsed).ToLocalChecked(); @@ -2579,7 +2603,7 @@ NAN_SETTER(Context2d::SetFont) { } } - PangoFontDescription *sys_desc = Canvas::ResolveFontDescription(desc); + PangoFontDescription *sys_desc = Canvas::ResolveFontDescription(desc, addon_data); pango_font_description_free(desc); if (size > 0) pango_font_description_set_absolute_size(sys_desc, size * PANGO_SCALE); diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index 8afb433d1..09f547f7f 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -7,6 +7,7 @@ #include "color.h" #include "nan.h" #include +#include "AddonData.h" typedef enum { TEXT_DRAW_PATHS, @@ -62,10 +63,7 @@ class Context2d: public Nan::ObjectWrap { canvas_state_t *states[CANVAS_MAX_STATES]; canvas_state_t *state; Context2d(Canvas *canvas); - static Nan::Persistent _DOMMatrix; - static Nan::Persistent _parseFont; - static Nan::Persistent constructor; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_METHOD(SaveExternalModules); static NAN_METHOD(DrawImage); diff --git a/src/Image.cc b/src/Image.cc index 103b65ee7..200630925 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -43,20 +43,21 @@ typedef struct { using namespace v8; -Nan::Persistent Image::constructor; +const char *Image::ctor_name = "Image"; /* * Initialize Image. */ void -Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData *addon_data) { Nan::HandleScope scope; - Local ctor = Nan::New(Image::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("Image").ToLocalChecked()); + Local data_holder = Nan::New(addon_data); + Local ctor = Nan::New(Image::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); + addon_data->image_ctor_tpl.Reset(ctor); // Prototype Local proto = ctor->PrototypeTemplate(); @@ -90,6 +91,7 @@ NAN_METHOD(Image::New) { Image *img = new Image; img->data_mode = DATA_IMAGE; img->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); Nan::Set(info.This(), Nan::New("onload").ToLocalChecked(), Nan::Null()).Check(); Nan::Set(info.This(), Nan::New("onerror").ToLocalChecked(), Nan::Null()).Check(); info.GetReturnValue().Set(info.This()); @@ -186,7 +188,9 @@ NAN_SETTER(Image::SetHeight) { */ NAN_METHOD(Image::GetSource){ - if (!Image::constructor.Get(info.GetIsolate())->HasInstance(info.This())) { + AddonData *addon_data = get_data_from_if1(info.This()); + Local ctor_tpl = Nan::New(addon_data->image_ctor_tpl); + if (!ctor_tpl->HasInstance(info.This())) { // #1534 Nan::ThrowTypeError("Method Image.GetSource called on incompatible receiver"); return; @@ -231,7 +235,9 @@ Image::clearData() { */ NAN_METHOD(Image::SetSource){ - if (!Image::constructor.Get(info.GetIsolate())->HasInstance(info.This())) { + AddonData *addon_data = get_data_from_if1(info.This()); + Local ctor_tpl = Nan::New(addon_data->image_ctor_tpl); + if (!ctor_tpl->HasInstance(info.This())) { // #1534 Nan::ThrowTypeError("Method Image.SetSource called on incompatible receiver"); return; diff --git a/src/Image.h b/src/Image.h index 62bc3f13b..8b99f11b6 100644 --- a/src/Image.h +++ b/src/Image.h @@ -8,6 +8,7 @@ #include #include // node < 7 uses libstdc++ on macOS which lacks complete c++11 #include +#include "AddonData.h" #ifdef HAVE_JPEG #include @@ -39,8 +40,8 @@ class Image: public Nan::ObjectWrap { char *filename; int width, height; int naturalWidth, naturalHeight; - static Nan::Persistent constructor; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static const char *ctor_name; + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_GETTER(GetComplete); static NAN_GETTER(GetWidth); diff --git a/src/ImageData.cc b/src/ImageData.cc index 668733d39..097141708 100644 --- a/src/ImageData.cc +++ b/src/ImageData.cc @@ -6,28 +6,29 @@ using namespace v8; -Nan::Persistent ImageData::constructor; +const char *ImageData::ctor_name = "ImageData"; /* * Initialize ImageData. */ void -ImageData::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) { +ImageData::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData *addon_data) { Nan::HandleScope scope; + Local data_holder = Nan::New(addon_data); // Constructor - Local ctor = Nan::New(ImageData::New); - constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("ImageData").ToLocalChecked()); + Local ctor = Nan::New(ImageData::New, data_holder); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); + addon_data->image_data_ctor_tpl.Reset(ctor); // Prototype Local proto = ctor->PrototypeTemplate(); SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, NULL, ctor); SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, NULL, ctor); Local ctx = Nan::GetCurrentContext(); - Nan::Set(target, Nan::New("ImageData").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); + Nan::Set(target, Nan::New(ctor_name).ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked()); } /* @@ -117,6 +118,7 @@ NAN_METHOD(ImageData::New) { ImageData *imageData = new ImageData(reinterpret_cast(*dataPtr), width, height); imageData->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); Nan::Set(info.This(), Nan::New("data").ToLocalChecked(), dataArray).Check(); info.GetReturnValue().Set(info.This()); } diff --git a/src/ImageData.h b/src/ImageData.h index 4832b37b2..887d19a60 100644 --- a/src/ImageData.h +++ b/src/ImageData.h @@ -5,11 +5,12 @@ #include #include // node < 7 uses libstdc++ on macOS which lacks complete c++11 #include +#include "AddonData.h" class ImageData: public Nan::ObjectWrap { public: - static Nan::Persistent constructor; - static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target); + static const char *ctor_name; + static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target, AddonData*); static NAN_METHOD(New); static NAN_GETTER(GetWidth); static NAN_GETTER(GetHeight); diff --git a/src/Util.h b/src/Util.h index dba6883a2..fb77f3203 100644 --- a/src/Util.h +++ b/src/Util.h @@ -3,6 +3,7 @@ #include #include #include +#include "AddonData.h" // Wrapper around Nan::SetAccessor that makes it easier to change the last // argument (signature). Getters/setters must be accessed only when there is @@ -32,3 +33,13 @@ inline bool streq_casein(std::string& str1, std::string& str2) { return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); } + +inline AddonData* get_data_from_if1(v8::Local obj) +{ + if (obj->InternalFieldCount() < 2) + return nullptr; + if (!obj->GetInternalField(1)->IsExternal()) + return nullptr; + + return reinterpret_cast(obj->GetInternalField(1).As()->Value()); +} \ No newline at end of file diff --git a/src/backend/Backend.cc b/src/backend/Backend.cc index 528f61a08..32670cefd 100644 --- a/src/backend/Backend.cc +++ b/src/backend/Backend.cc @@ -21,6 +21,7 @@ void Backend::init(const Nan::FunctionCallbackInfo &info) { Backend *backend = construct(width, height); backend->Wrap(info.This()); + info.This()->SetInternalField(1, info.Data()); info.GetReturnValue().Set(info.This()); } diff --git a/src/backend/ImageBackend.cc b/src/backend/ImageBackend.cc index d354d92cc..c83fa0d25 100644 --- a/src/backend/ImageBackend.cc +++ b/src/backend/ImageBackend.cc @@ -55,17 +55,18 @@ void ImageBackend::setFormat(cairo_format_t _format) { this->format = _format; } -Nan::Persistent ImageBackend::constructor; +const char *ImageBackend::ctor_name = "ImageBackend"; -void ImageBackend::Initialize(Local target) { +void ImageBackend::Initialize(Local target, AddonData *addon_data) { Nan::HandleScope scope; Local ctor = Nan::New(ImageBackend::New); - ImageBackend::constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("ImageBackend").ToLocalChecked()); + + addon_data->image_backend_ctor_tpl.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); Nan::Set(target, - Nan::New("ImageBackend").ToLocalChecked(), + Nan::New(ctor_name).ToLocalChecked(), Nan::GetFunction(ctor).ToLocalChecked()).Check(); } diff --git a/src/backend/ImageBackend.h b/src/backend/ImageBackend.h index f68dacfdb..b89f8787f 100644 --- a/src/backend/ImageBackend.h +++ b/src/backend/ImageBackend.h @@ -1,6 +1,7 @@ #pragma once #include "Backend.h" +#include "../AddonData.h" #include class ImageBackend : public Backend @@ -19,8 +20,8 @@ class ImageBackend : public Backend int32_t approxBytesPerPixel(); - static Nan::Persistent constructor; - static void Initialize(v8::Local target); + static const char *ctor_name; + static void Initialize(v8::Local target, AddonData*); static NAN_METHOD(New); const static cairo_format_t DEFAULT_FORMAT = CAIRO_FORMAT_ARGB32; }; diff --git a/src/backend/PdfBackend.cc b/src/backend/PdfBackend.cc index d8bd23422..83d80d07a 100644 --- a/src/backend/PdfBackend.cc +++ b/src/backend/PdfBackend.cc @@ -34,17 +34,19 @@ cairo_surface_t* PdfBackend::recreateSurface() { } -Nan::Persistent PdfBackend::constructor; +const char *PdfBackend::ctor_name = "PdfBackend"; -void PdfBackend::Initialize(Local target) { +void PdfBackend::Initialize(Local target, AddonData *addon_data) { Nan::HandleScope scope; - Local ctor = Nan::New(PdfBackend::New); - PdfBackend::constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("PdfBackend").ToLocalChecked()); + Local data_holder = Nan::New(addon_data); + Local ctor = Nan::New(PdfBackend::New, data_holder); + + addon_data->pdf_backend_ctor_tpl.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); Nan::Set(target, - Nan::New("PdfBackend").ToLocalChecked(), + Nan::New(ctor_name).ToLocalChecked(), Nan::GetFunction(ctor).ToLocalChecked()).Check(); } diff --git a/src/backend/PdfBackend.h b/src/backend/PdfBackend.h index 03656f500..942794fb3 100644 --- a/src/backend/PdfBackend.h +++ b/src/backend/PdfBackend.h @@ -2,6 +2,7 @@ #include "Backend.h" #include "../closure.h" +#include "../AddonData.h" #include class PdfBackend : public Backend @@ -18,7 +19,7 @@ class PdfBackend : public Backend ~PdfBackend(); static Backend *construct(int width, int height); - static Nan::Persistent constructor; - static void Initialize(v8::Local target); + static const char* ctor_name; + static void Initialize(v8::Local target, AddonData*); static NAN_METHOD(New); }; diff --git a/src/backend/SvgBackend.cc b/src/backend/SvgBackend.cc index 10bf4caa7..2e6cde1c9 100644 --- a/src/backend/SvgBackend.cc +++ b/src/backend/SvgBackend.cc @@ -42,17 +42,18 @@ cairo_surface_t* SvgBackend::recreateSurface() { } -Nan::Persistent SvgBackend::constructor; +const char *SvgBackend::ctor_name = "SvgBackend"; -void SvgBackend::Initialize(Local target) { +void SvgBackend::Initialize(Local target, AddonData *addon_data) { Nan::HandleScope scope; Local ctor = Nan::New(SvgBackend::New); - SvgBackend::constructor.Reset(ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(Nan::New("SvgBackend").ToLocalChecked()); + + addon_data->svg_backend_ctor_tpl.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(2); + ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked()); Nan::Set(target, - Nan::New("SvgBackend").ToLocalChecked(), + Nan::New(ctor_name).ToLocalChecked(), Nan::GetFunction(ctor).ToLocalChecked()).Check(); } diff --git a/src/backend/SvgBackend.h b/src/backend/SvgBackend.h index 6377b438b..65754830c 100644 --- a/src/backend/SvgBackend.h +++ b/src/backend/SvgBackend.h @@ -2,6 +2,7 @@ #include "Backend.h" #include "../closure.h" +#include "../AddonData.h" #include class SvgBackend : public Backend @@ -18,7 +19,7 @@ class SvgBackend : public Backend ~SvgBackend(); static Backend *construct(int width, int height); - static Nan::Persistent constructor; - static void Initialize(v8::Local target); + static const char *ctor_name; + static void Initialize(v8::Local target, AddonData*); static NAN_METHOD(New); }; diff --git a/src/init.cc b/src/init.cc index 816ba5837..a0593d86d 100644 --- a/src/init.cc +++ b/src/init.cc @@ -33,13 +33,14 @@ using namespace v8; #endif NAN_MODULE_INIT(init) { - Backends::Initialize(target); - Canvas::Initialize(target); - Image::Initialize(target); - ImageData::Initialize(target); - Context2d::Initialize(target); - Gradient::Initialize(target); - Pattern::Initialize(target); + AddonData *data = new AddonData(); + Backends::Initialize(target, data); + Canvas::Initialize(target, data); + Image::Initialize(target, data); + ImageData::Initialize(target, data); + Context2d::Initialize(target, data); + Gradient::Initialize(target, data); + Pattern::Initialize(target, data); Nan::Set(target, Nan::New("cairoVersion").ToLocalChecked(), Nan::New(cairo_version_string()).ToLocalChecked()).Check(); #ifdef HAVE_JPEG @@ -89,4 +90,7 @@ NAN_MODULE_INIT(init) { Nan::Set(target, Nan::New("freetypeVersion").ToLocalChecked(), Nan::New(freetype_version).ToLocalChecked()).Check(); } -NODE_MODULE(canvas, init); +NODE_MODULE_INIT() +{ + init(exports); +} diff --git a/src/lock.cc b/src/lock.cc new file mode 100644 index 000000000..dbb877b93 --- /dev/null +++ b/src/lock.cc @@ -0,0 +1,61 @@ +#include "lock.h" + +UVLockHandle::UVLockHandle() +{ + uv_rwlock_init(&rw_lock); + uv_mutex_init(&mutex); +} + +UVLockHandle::~UVLockHandle() +{ + uv_rwlock_destroy(&rw_lock); + uv_mutex_destroy(&mutex); +} + +UVLocker::UVLocker(UVLockHandle &rwlock, LockerType t) : _type{t} +{ + handle = &rwlock; + + switch (t) + { + case rd: + uv_rwlock_rdlock(&handle->rw_lock); + break; + + case wr: + uv_rwlock_wrlock(&handle->rw_lock); + break; + + case mutex: + uv_mutex_lock(&handle->mutex); + break; + } +} + +UVLocker::~UVLocker() +{ + clean(); +} + +void UVLocker::clean() +{ + if (handle == nullptr) + return; + + switch (_type) + { + case rd: + uv_rwlock_rdunlock(&handle->rw_lock); + break; + + case wr: + uv_rwlock_wrunlock(&handle->rw_lock); + break; + + case mutex: + uv_mutex_unlock(&handle->mutex); + break; + } + + handle = nullptr; +} \ No newline at end of file diff --git a/src/lock.h b/src/lock.h new file mode 100644 index 000000000..7b973129e --- /dev/null +++ b/src/lock.h @@ -0,0 +1,34 @@ +#include "uv.h" + +class UVLockHandle +{ +public: + uv_rwlock_t rw_lock; + uv_mutex_t mutex; + UVLockHandle(); + ~UVLockHandle(); +}; + +/** + * [RAII] Promise to unlock when there are exception throw + */ +class UVLocker +{ + +public: + enum LockerType + { + rd = 0, + wr, + mutex + }; + + UVLocker(UVLockHandle &, LockerType); + ~UVLocker(); + + void clean(); + +private: + UVLockHandle *handle; + LockerType _type; +}; \ No newline at end of file diff --git a/test/worker.js b/test/worker.js new file mode 100644 index 000000000..bfd54da6c --- /dev/null +++ b/test/worker.js @@ -0,0 +1,47 @@ +const { resolve } = require("path"); +const { createCanvas, registerFont } = require("../"); + +function create({ width, height } = { width: 800, height: 600 }) { + let canvas, context; + canvas = createCanvas(width, height); + context = canvas.getContext("2d"); + + return { + canvas, + context, + }; +} + +exports.getCanvasSize = ({ width, height }) => { + const { canvas, context } = create({ + width, + height, + }); + + return { + width: canvas.width, + height: canvas.height, + }; +}; + +exports.updateContext = (options) => { + const { canvas, context } = create(); + + for (const key in options) { + context[key] = options[key]; + } + + return Object.keys(options).reduce( + (acm, cur) => ({ + ...acm, + [cur]: context[cur], + }), + {} + ); +}; + +exports.registerFont = (options) => { + registerFont(options.path, { + family: options.fontFamily, + }); +}; diff --git a/test/worker.test.js b/test/worker.test.js new file mode 100644 index 000000000..2610c6325 --- /dev/null +++ b/test/worker.test.js @@ -0,0 +1,73 @@ +const assert = require("assert"); +const { resolve } = require("path"); +const Piscina = require("piscina"); + +const filename = resolve(__dirname, "./worker.js"); + +describe("worker:Canvas", () => { + it("create success", async () => { + const pool = new Piscina({ + filename, + }); + + const first = await pool.run( + { width: 800, height: 600 }, + { name: "getCanvasSize" } + ); + + const second = await pool.run( + { width: 900, height: 500 }, + { name: "getCanvasSize" } + ); + + assert( + first.width == 800 && first.height == 600, + "invalid first canvas size" + ); + assert( + second.width == 900 && second.height == 500, + "invalid second canvas size" + ); + }); + + it("context are independent", async () => { + const pool = new Piscina({ + filename, + }); + const ctx1 = { + font: "20px Helvetica", + fill: "red", + }; + const ctx2 = { + fill: "green", + }; + const ctxResult1 = await pool.run(ctx1, { + name: "updateContext", + }); + const ctxResult2 = await pool.run(ctx2, { name: "updateContext" }); + + assert(ctxResult1.font === ctx1.font, "ctx1 font didn't match"); + assert(ctxResult1.fill === ctx1.fill, "ctx1 fill did't match"); + assert(ctxResult2.fill === ctx2.fill, "ctx2 fill did't match"); + }); + + it("register font", async () => { + const pool = new Piscina({ + filename, + }); + pool.run( + { + path: resolve(__dirname, "../examples/pfennigFont/Pfennig.ttf"), + fontFamily: "pfenning", + }, + { name: "registerFont" } + ); + pool.run( + { + path: resolve(__dirname, "../examples/pfennigFont/PfennigItalic.ttf"), + fontFamily: "pfenning_italic", + }, + { name: "registerFont" } + ); + }); +});