Skip to content
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

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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`.
Expand Down
2 changes: 2 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
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
Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -21,6 +21,7 @@
#include "Util.h"
#include <vector>
#include "node_buffer.h"
#include "lock.h"

#ifdef HAVE_JPEG
#include "JPEGStream.h"
Expand All @@ -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();
Expand All @@ -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());
}

Expand All @@ -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;
Expand All @@ -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");
Expand All @@ -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);

Expand Down Expand Up @@ -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);
});
Expand All @@ -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);
Expand All @@ -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");
}

Expand Down Expand Up @@ -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
Expand All @@ -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)) {
Expand All @@ -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;
Expand Down
17 changes: 4 additions & 13 deletions src/Canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <v8.h>
#include <vector>
#include <cstddef>
#include "AddonData.h"

/*
* Maxmimum states per context.
Expand All @@ -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);
Expand All @@ -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(); }
Expand Down
16 changes: 9 additions & 7 deletions src/CanvasGradient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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(
Expand Down
6 changes: 4 additions & 2 deletions src/CanvasGradient.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading