Skip to content

Support worker env.[aka: context aware] #1981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 2 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -59,6 +59,8 @@
'target_name': 'canvas',
'include_dirs': ["<!(node -e \"require('nan')\")"],
'sources': [
'src/AddonData.cc',
'src/lock.cc',
'src/backend/Backend.cc',
'src/backend/ImageBackend.cc',
'src/backend/PdfBackend.cc',
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@
"dtslint": "^4.0.7",
"express": "^4.16.3",
"mocha": "^5.2.0",
"piscina": "^3.2.0",
"pixelmatch": "^4.0.2",
"standard": "^12.0.1",
"typescript": "^4.2.2"
25 changes: 25 additions & 0 deletions src/AddonData.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "AddonData.h"

AddonData::AddonData()
{
node::AddEnvironmentCleanupHook(v8::Isolate::GetCurrent(), Dispose, this);
}

void AddonData::Dispose(void* ins)
{
AddonData *data = static_cast<AddonData*>(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;
}
36 changes: 36 additions & 0 deletions src/AddonData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <pango/pangocairo.h>
#include <v8.h>
#include <nan.h>

/*
* 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<v8::FunctionTemplate> canvas_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> gradient_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> context2d_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> image_data_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> image_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> pattern_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> image_backend_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> pdf_backend_ctor_tpl;
Nan::Persistent<v8::FunctionTemplate> svg_backend_ctor_tpl;
Nan::Persistent<v8::Function> context2d_dom_matrix;
Nan::Persistent<v8::Function> context2d_parse_font;
Nan::Persistent<v8::Function> pattern_dom_matrix;

AddonData();

static void Dispose(void*);
};
8 changes: 4 additions & 4 deletions src/Backends.cc
Original file line number Diff line number Diff line change
@@ -6,13 +6,13 @@

using namespace v8;

void Backends::Initialize(Local<Object> target) {
void Backends::Initialize(Local<Object> target, AddonData* data) {
Nan::HandleScope scope;

Local<Object> obj = Nan::New<Object>();
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<String>("Backends").ToLocalChecked(), obj).Check();
}
3 changes: 2 additions & 1 deletion src/Backends.h
Original file line number Diff line number Diff line change
@@ -3,8 +3,9 @@
#include "backend/Backend.h"
#include <nan.h>
#include <v8.h>
#include "AddonData.h"

class Backends : public Nan::ObjectWrap {
public:
static void Initialize(v8::Local<v8::Object> target);
static void Initialize(v8::Local<v8::Object> target, AddonData*);
};
52 changes: 37 additions & 15 deletions src/Canvas.cc
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
#include "Util.h"
#include <vector>
#include "node_buffer.h"
#include "lock.h"

#ifdef HAVE_JPEG
#include "JPEGStream.h"
@@ -38,23 +39,26 @@
using namespace v8;
using namespace std;

Nan::Persistent<FunctionTemplate> Canvas::constructor;
const char *Canvas::ctor_name = "Canvas";

// these variables are protected by uv_rwlock;
UVLockHandle rwlock_handle;
std::vector<FontFace> font_face_list;

/*
* Initialize Canvas.
*/

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<External> data_holder = Nan::New<External>(addon_data);
// Constructor
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Canvas::New);
constructor.Reset(ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(Nan::New("Canvas").ToLocalChecked());
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Canvas::New, data_holder);
ctor->InstanceTemplate()->SetInternalFieldCount(2);
ctor->SetClassName(Nan::New(ctor_name).ToLocalChecked());
addon_data->canvas_ctor_tpl.Reset(ctor);

// Prototype
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
@@ -78,12 +82,12 @@ Canvas::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
Nan::SetTemplate(proto, "PNG_ALL_FILTERS", Nan::New<Uint32>(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<Context> 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<AddonData*>(info.Data().As<External>()->Value());
Backend* backend = NULL;
if (info[0]->IsNumber()) {
int width = Nan::To<uint32_t>(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<FunctionTemplate> image_backend = Nan::New(addon_data->image_backend_ctor_tpl);
Local<FunctionTemplate> pdf_backend = Nan::New(addon_data->pdf_backend_ctor_tpl);
Local<FunctionTemplate> 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<Backend>(Nan::To<Object>(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;
17 changes: 4 additions & 13 deletions src/Canvas.h
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
#include <v8.h>
#include <vector>
#include <cstddef>
#include "AddonData.h"

/*
* Maxmimum states per context.
@@ -20,25 +21,15 @@
#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.
*/

class Canvas: public Nan::ObjectWrap {
public:
static Nan::Persistent<v8::FunctionTemplate> 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(); }
16 changes: 9 additions & 7 deletions src/CanvasGradient.cc
Original file line number Diff line number Diff line change
@@ -7,27 +7,28 @@

using namespace v8;

Nan::Persistent<FunctionTemplate> 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<External> data_holder = Nan::New<External>(addon_data);
// Constructor
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Gradient::New);
constructor.Reset(ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(Nan::New("CanvasGradient").ToLocalChecked());
Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(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<Context> 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(
6 changes: 4 additions & 2 deletions src/CanvasGradient.h
Original file line number Diff line number Diff line change
@@ -5,11 +5,13 @@
#include <nan.h>
#include <v8.h>
#include <cairo.h>
#include <map>
#include "AddonData.h"

class Gradient: public Nan::ObjectWrap {
public:
static Nan::Persistent<v8::FunctionTemplate> 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);
Loading