Browse Source

Initial commit of extracted PNG creation.

v1.x
King Koopa 11 years ago
committed by TJ Holowaychuk
parent
commit
c3ed7faca1
  1. 37
      src/Canvas.cc
  2. 225
      src/PNG.h
  3. 10
      src/closure.h
  4. 194
      src/nan.h

37
src/Canvas.cc

@ -5,6 +5,7 @@
//
#include "Canvas.h"
#include "PNG.h"
#include "CanvasRenderingContext2d.h"
#include <assert.h>
#include <stdlib.h>
@ -128,31 +129,31 @@ NAN_SETTER(Canvas::SetHeight) {
static cairo_status_t
toBuffer(void *c, const uint8_t *data, unsigned len) {
closure_t *closure = (closure_t *) c;
// Olaf: grow buffer
if (closure->len + len > closure->max_len) {
uint8_t *data;
unsigned max;
// round to the nearest multiple of 1024 bytes
max = (closure->max_len + len + 1023) & ~1023;
unsigned max = closure->max_len;
do {
max *= 2;
} while (closure->len + len > max);
data = (uint8_t *) realloc(closure->data, max);
if (!data) return CAIRO_STATUS_NO_MEMORY;
closure->data = data;
closure->max_len = max;
}
memcpy(closure->data + closure->len, data, len);
closure->len += len;
return CAIRO_STATUS_SUCCESS;
}
/*
* EIO toBuffer callback.
*/
#if NODE_VERSION_AT_LEAST(0, 6, 0)
void
Canvas::ToBufferAsync(uv_work_t *req) {
@ -169,7 +170,7 @@ Canvas::EIO_ToBuffer(eio_req *req) {
closure->canvas->surface()
, toBuffer
, closure);
#if !NODE_VERSION_AT_LEAST(0, 5, 4)
return 0;
#endif
@ -209,14 +210,14 @@ Canvas::EIO_AfterToBuffer(eio_req *req) {
delete closure->pfn;
closure_destroy(closure);
free(closure);
#if !NODE_VERSION_AT_LEAST(0, 6, 0)
return 0;
#endif
}
/*
* Convert PNG data to a node::Buffer, async when a
* Convert PNG data to a node::Buffer, async when a
* callback function is passed.
*/
@ -249,7 +250,7 @@ NAN_METHOD(Canvas::ToBuffer) {
// TODO: only one callback fn in closure
canvas->Ref();
closure->pfn = new NanCallback(args[0].As<Function>());
#if NODE_VERSION_AT_LEAST(0, 6, 0)
uv_work_t* req = new uv_work_t;
req->data = closure;
@ -258,7 +259,7 @@ NAN_METHOD(Canvas::ToBuffer) {
eio_custom(EIO_ToBuffer, EIO_PRI_DEFAULT, EIO_AfterToBuffer, closure);
ev_ref(EV_DEFAULT_UC);
#endif
NanReturnUndefined();
// Sync
} else {
@ -272,7 +273,7 @@ NAN_METHOD(Canvas::ToBuffer) {
}
TryCatch try_catch;
status = cairo_surface_write_to_png_stream(canvas->surface(), toBuffer, &closure);
status = canvas_write_to_png_stream(canvas->surface(), toBuffer, &closure);
if (try_catch.HasCaught()) {
closure_destroy(&closure);
@ -320,7 +321,9 @@ NAN_METHOD(Canvas::StreamPNGSync) {
closure.fn = Handle<Function>::Cast(args[0]);
TryCatch try_catch;
cairo_status_t status = cairo_surface_write_to_png_stream(canvas->surface(), streamPNG, &closure);
//TODO: use libpng directly
cairo_status_t status = canvas_write_to_png_stream(canvas->surface(), streamPNG, &closure);
if (try_catch.HasCaught()) {
NanReturnValue(try_catch.ReThrow());

225
src/PNG.h

@ -0,0 +1,225 @@
#ifndef _CANVAS_PNG_H
#define _CANVAS_PNG_H
#include <png.h>
#include <pngconf.h>
#include <cairo.h>
#include <stdlib.h>
#pragma once
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
#define likely(expr) (__builtin_expect (!!(expr), 1))
#define unlikely(expr) (__builtin_expect (!!(expr), 0))
#else
#define likely(expr) (expr)
#define unlikely(expr) (expr)
#endif
static void pngtest_flush(png_structp png_ptr) {
/* Do nothing; fflush() is said to be just a waste of energy. */
(void) png_ptr; /* Stifle compiler warning */
}
/* Converts native endian xRGB => RGBx bytes */
static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) {
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *b = &data[i];
uint32_t pixel;
memcpy(&pixel, b, sizeof (uint32_t));
b[0] = (pixel & 0xff0000) >> 16;
b[1] = (pixel & 0x00ff00) >> 8;
b[2] = (pixel & 0x0000ff) >> 0;
b[3] = 0;
}
}
/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
static void unpremultiply_data(png_structp png, png_row_infop row_info, png_bytep data) {
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *b = &data[i];
uint32_t pixel;
uint8_t alpha;
memcpy(&pixel, b, sizeof (uint32_t));
alpha = (pixel & 0xff000000) >> 24;
if (alpha == 0) {
b[0] = b[1] = b[2] = b[3] = 0;
} else {
b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
b[3] = alpha;
}
}
}
static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr write_func, void *closure) {
unsigned int i;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
uint8_t *data;
png_structp png;
png_infop info;
// png_infop end_info;
png_bytep *volatile rows = NULL;
png_color_16 white;
int png_color_type;
int bpc;
unsigned int width = cairo_image_surface_get_width(surface);
unsigned int height = cairo_image_surface_get_height(surface);
data = cairo_image_surface_get_data(surface);
if (data == NULL) {
status = CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
return status;
}
cairo_surface_flush(surface);
if (width == 0 || height == 0) {
status = CAIRO_STATUS_WRITE_ERROR;
return status;
}
rows = (png_bytep *) malloc(height * sizeof (png_byte*));
if (unlikely(rows == NULL)) {
status = CAIRO_STATUS_NO_MEMORY;
return status;
}
for (i = 0; i < height; i++) {
rows[i] = (png_byte *) data + i * cairo_image_surface_get_stride(surface);
}
#ifdef PNG_USER_MEM_SUPPORTED
png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, NULL, NULL);
#else
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
if (unlikely(png == NULL)) {
status = CAIRO_STATUS_NO_MEMORY;
free(rows);
return status;
}
info = png_create_info_struct (png);
if (unlikely(info == NULL)) {
status = CAIRO_STATUS_NO_MEMORY;
png_destroy_write_struct(&png, &info);
free(rows);
return status;
}
#ifdef PNG_SETJMP_SUPPORTED
if (setjmp (png_jmpbuf (png))) {
png_destroy_write_struct(&png, &info);
free(rows);
return status;
}
#endif
png_set_write_fn(png, closure, write_func, pngtest_flush);
switch (cairo_image_surface_get_format(surface)) {
case CAIRO_FORMAT_ARGB32:
bpc = 8;
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case CAIRO_FORMAT_RGB30:
bpc = 10;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_RGB24:
bpc = 8;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_A8:
bpc = 8;
png_color_type = PNG_COLOR_TYPE_GRAY;
break;
case CAIRO_FORMAT_A1:
bpc = 1;
png_color_type = PNG_COLOR_TYPE_GRAY;
#ifndef WORDS_BIGENDIAN
png_set_packswap(png);
#endif
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_RGB16_565:
default:
status = CAIRO_STATUS_INVALID_FORMAT;
png_destroy_write_struct(&png, &info);
free(rows);
return status;
}
png_set_IHDR(png, info, width, height, bpc, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
white.gray = (1 << bpc) - 1;
white.red = white.blue = white.green = white.gray;
png_set_bKGD(png, info, &white);
/* We have to call png_write_info() before setting up the write
* transformation, since it stores data internally in 'png'
* that is needed for the write transformation functions to work.
*/
png_write_info(png, info);
if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
png_set_write_user_transform_fn(png, unpremultiply_data);
} else if (png_color_type == PNG_COLOR_TYPE_RGB) {
png_set_write_user_transform_fn(png, convert_data_to_bytes);
png_set_filler(png, 0, PNG_FILLER_AFTER);
}
png_write_image (png, rows);
png_write_end (png, info);
png_destroy_write_struct (&png, &info);
free(rows);
return status;
}
struct png_write_closure_t {
cairo_write_func_t write_func;
void *closure;
};
static void stream_write_func(png_structp png, png_bytep data, png_size_t size) {
cairo_status_t status;
struct png_write_closure_t *png_closure;
png_closure = (struct png_write_closure_t *) png_get_io_ptr(png);
status = png_closure->write_func(png_closure->closure, data, size);
if (unlikely(status)) {
cairo_status_t *error = (cairo_status_t *) png_get_error_ptr(png);
if (*error == CAIRO_STATUS_SUCCESS) {
*error = status;
}
png_error(png, NULL);
}
}
static cairo_status_t canvas_write_to_png_stream(cairo_surface_t *surface, cairo_write_func_t write_func, void *closure) {
struct png_write_closure_t png_closure;
if (cairo_surface_status(surface)) {
return cairo_surface_status(surface);
}
// if (surface->finished) {
// return CAIRO_STATUS_SURFACE_FINISHED;
// }
png_closure.write_func = write_func;
png_closure.closure = closure;
return canvas_write_png(surface, stream_write_func, &png_closure);
}
#endif

10
src/closure.h

@ -8,6 +8,14 @@
#ifndef __NODE_CLOSURE_H__
#define __NODE_CLOSURE_H__
#ifdef __unix__
#include<sys/user.h>
#endif
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
#include "nan.h"
/*
@ -32,7 +40,7 @@ cairo_status_t
closure_init(closure_t *closure, Canvas *canvas) {
closure->len = 0;
closure->canvas = canvas;
closure->data = (uint8_t *) malloc(closure->max_len = 1024);
closure->data = (uint8_t *) malloc(closure->max_len = PAGE_SIZE);
if (!closure->data) return CAIRO_STATUS_NO_MEMORY;
return CAIRO_STATUS_SUCCESS;
}

194
src/nan.h

@ -1,12 +1,18 @@
/**********************************************************************************
* NAN - Native Abstractions for Node.js
*
* Copyright (c) 2013 Rod Vagg
* Copyright (c) 2013 NAN contributors:
* - Rod Vagg <https://github.com/rvagg>
* - King Koopa <https://github.com/kkoopa>
* - Trevor Norris <https://github.com/trevnorris>
*
* MIT +no-false-attribs License <https://github.com/rvagg/nan/blob/master/LICENSE>
*
* Version 0.1.0 (current Node unstable: 0.11.4)
* Version 0.2.0 (current Node unstable: 0.11.4)
*
* Changelog:
* * 0.2.0 .... TODO
*
* * 0.1.0 Jul 21 2013
* - Added `NAN_GETTER`, `NAN_SETTER`
* - Added `NanThrowError` with single Local<Value> argument
@ -77,32 +83,71 @@ static inline uint32_t NanUInt32OptionValue(
static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
# define NAN_METHOD(name) \
void name(const v8::FunctionCallbackInfo<v8::Value>& args)
# define _NAN_METHOD_ARGS const v8::FunctionCallbackInfo<v8::Value>& args
# define NAN_METHOD(name) void name(_NAN_METHOD_ARGS)
# define _NAN_GETTER_ARGS const v8::PropertyCallbackInfo<v8::Value>& args
# define NAN_GETTER(name) \
void name( \
v8::Local<v8::String> property \
, const v8::PropertyCallbackInfo<v8::Value>& args)
void name(v8::Local<v8::String> property, _NAN_GETTER_ARGS)
# define _NAN_SETTER_ARGS const v8::PropertyCallbackInfo<void>& args
# define NAN_SETTER(name) \
void name( \
v8::Local<v8::String> property \
, v8::Local<v8::Value> value \
, const v8::PropertyCallbackInfo<void>& args)
, _NAN_SETTER_ARGS)
# define _NAN_PROPERTY_GETTER_ARGS \
const v8::PropertyCallbackInfo<v8::Value>& args
# define NAN_PROPERTY_GETTER(name) \
void name(v8::Local<v8::String> property \
, _NAN_PROPERTY_GETTER_ARGS)
# define _NAN_PROPERTY_SETTER_ARGS \
const v8::PropertyCallbackInfo<v8::Value>& args
# define NAN_PROPERTY_SETTER(name) \
void name(v8::Local<v8::String> property \
, v8::Local<v8::Value> value \
, _NAN_PROPERTY_SETTER_ARGS)
# define _NAN_PROPERTY_ENUMERATOR_ARGS \
const v8::PropertyCallbackInfo<v8::Array>& args
# define NAN_PROPERTY_ENUMERATOR(name) \
void name(_NAN_PROPERTY_ENUMERATOR_ARGS)
# define _NAN_PROPERTY_DELETER_ARGS \
const v8::PropertyCallbackInfo<v8::Boolean>& args
# define NAN_PROPERTY_DELETER(name) \
void name( \
v8::Local<v8::String> property \
, _NAN_PROPERTY_DELETER_ARGS)
# define _NAN_PROPERTY_QUERY_ARGS \
const v8::PropertyCallbackInfo<v8::Integer>& args
# define NAN_PROPERTY_QUERY(name) \
void name(v8::Local<v8::String> property, _NAN_PROPERTY_QUERY_ARGS)
# define NanGetInternalFieldPointer(object, index) \
object->GetAlignedPointerFromInternalField(index)
# define NanSetInternalFieldPointer(object, index, value) \
object->SetAlignedPointerInInternalField(index, value)
# define NAN_WEAK_CALLBACK(type, name) \
void name( \
v8::Isolate* isolate, \
v8::Persistent<v8::Object>* object, \
type data)
# define NAN_WEAK_CALLBACK_OBJECT (*object)
# define NAN_WEAK_CALLBACK_DATA(type) ((type) data)
# define NanScope() v8::HandleScope scope(nan_isolate)
# define NanReturnValue(value) return args.GetReturnValue().Set(value);
# define NanReturnUndefined() return;
# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj);
# define NanReturnValue(value) return args.GetReturnValue().Set(value)
# define NanReturnUndefined() return
# define NanAssignPersistent(type, handle, obj) handle.Reset(nan_isolate, obj)
# define NanObjectWrapHandle(obj) obj->handle()
# define NanMakeWeak(handle, parameter, callback) \
handle.MakeWeak(nan_isolate, parameter, callback)
# define THROW_ERROR(fun, errmsg) \
# define _NAN_THROW_ERROR(fun, errmsg) \
do { \
NanScope(); \
v8::ThrowException(fun(v8::String::New(errmsg))); \
} while (0);
inline static void NanThrowError(const char* errmsg) {
THROW_ERROR(v8::Exception::Error, errmsg);
_NAN_THROW_ERROR(v8::Exception::Error, errmsg);
}
inline static void NanThrowError(v8::Local<v8::Value> error) {
@ -111,17 +156,25 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
}
inline static void NanThrowTypeError(const char* errmsg) {
THROW_ERROR(v8::Exception::TypeError, errmsg);
_NAN_THROW_ERROR(v8::Exception::TypeError, errmsg);
}
inline static void NanThrowRangeError(const char* errmsg) {
THROW_ERROR(v8::Exception::RangeError, errmsg);
_NAN_THROW_ERROR(v8::Exception::RangeError, errmsg);
}
static inline void NanDispose(v8::Persistent<v8::Object> &handle) {
template<class T> static inline void NanDispose(v8::Persistent<T> &handle) {
handle.Dispose(nan_isolate);
}
static inline v8::Local<v8::Object> NanNewBufferHandle (
char *data,
size_t length,
node::smalloc::FreeCallback callback,
void *hint) {
return node::Buffer::New(data, length, callback, hint);
}
static inline v8::Local<v8::Object> NanNewBufferHandle (
char *data, uint32_t size) {
return node::Buffer::New(data, size);
@ -131,6 +184,10 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
return node::Buffer::New(size);
}
static inline v8::Local<v8::Object> NanBufferUse(char* data, uint32_t size) {
return node::Buffer::Use(data, size);
}
template <class TypeName>
inline v8::Local<TypeName> NanPersistentToLocal(
const v8::Persistent<TypeName>& persistent) {
@ -148,36 +205,78 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
return NanPersistentToLocal(function_template)->HasInstance(value);
}
static inline v8::Local<v8::Context> NanNewContextHandle(
v8::ExtensionConfiguration* extensions = NULL,
v8::Handle<v8::ObjectTemplate> g_template = v8::Handle<v8::ObjectTemplate>(),
v8::Handle<v8::Value> g_object = v8::Handle<v8::Value>()) {
return v8::Local<v8::Context>::New(nan_isolate, v8::Context::New(
nan_isolate, extensions, g_template, g_object));
}
#else
// Node 0.8 and 0.10
# define NAN_METHOD(name) \
v8::Handle<v8::Value> name(const v8::Arguments& args)
# define _NAN_METHOD_ARGS const v8::Arguments& args
# define NAN_METHOD(name) v8::Handle<v8::Value> name(_NAN_METHOD_ARGS)
# define _NAN_GETTER_ARGS const v8::AccessorInfo &args
# define NAN_GETTER(name) \
v8::Handle<v8::Value> name( \
v8::Local<v8::String> property \
, const v8::AccessorInfo &args)
v8::Handle<v8::Value> name(v8::Local<v8::String> property, _NAN_GETTER_ARGS)
# define _NAN_SETTER_ARGS const v8::AccessorInfo &args
# define NAN_SETTER(name) \
void name( \
v8::Local<v8::String> property \
, v8::Local<v8::Value> value \
, const v8::AccessorInfo &args)
v8::Local<v8::String> property \
, v8::Local<v8::Value> value \
, _NAN_SETTER_ARGS)
# define _NAN_PROPERTY_GETTER_ARGS const v8::AccessorInfo& args
# define NAN_PROPERTY_GETTER(name) \
v8::Handle<v8::Value> name(v8::Local<v8::String> property \
, _NAN_PROPERTY_GETTER_ARGS)
# define _NAN_PROPERTY_SETTER_ARGS const v8::AccessorInfo& args
# define NAN_PROPERTY_SETTER(name) \
v8::Handle<v8::Value> name(v8::Local<v8::String> property \
, v8::Local<v8::Value> value \
, _NAN_PROPERTY_SETTER_ARGS)
# define _NAN_PROPERTY_ENUMERATOR_ARGS const v8::AccessorInfo& args
# define NAN_PROPERTY_ENUMERATOR(name) \
v8::Handle<v8::Array> name(_NAN_PROPERTY_ENUMERATOR_ARGS)
# define _NAN_PROPERTY_DELETER_ARGS const v8::AccessorInfo& args
# define NAN_PROPERTY_DELETER(name) \
v8::Handle<v8::Boolean> name( \
v8::Local<v8::String> property \
, _NAN_PROPERTY_DELETER_ARGS)
# define _NAN_PROPERTY_QUERY_ARGS const v8::AccessorInfo& args
# define NAN_PROPERTY_QUERY(name) \
v8::Handle<v8::Integer> name( \
v8::Local<v8::String> property \
, _NAN_PROPERTY_QUERY_ARGS)
# define NanGetInternalFieldPointer(object, index) \
object->GetPointerFromInternalField(index)
# define NanSetInternalFieldPointer(object, index, value) \
object->SetPointerInInternalField(index, value)
# define NAN_WEAK_CALLBACK(type, name) void name( \
v8::Persistent<v8::Value> object, \
void *data)
# define NAN_WEAK_CALLBACK_OBJECT object
# define NAN_WEAK_CALLBACK_DATA(type) ((type) data)
# define NanScope() v8::HandleScope scope
# define NanReturnValue(value) return scope.Close(value);
# define NanReturnUndefined() return v8::Undefined();
# define NanReturnValue(value) return scope.Close(value)
# define NanReturnUndefined() return v8::Undefined()
# define NanAssignPersistent(type, handle, obj) \
handle = v8::Persistent<type>::New(obj);
handle = v8::Persistent<type>::New(obj)
# define NanObjectWrapHandle(obj) obj->handle_
# define NanMakeWeak(handle, parameters, callback) \
handle.MakeWeak(parameters, callback)
# define THROW_ERROR(fun, errmsg) \
# define _NAN_THROW_ERROR(fun, errmsg) \
do { \
NanScope(); \
return v8::ThrowException(fun(v8::String::New(errmsg))); \
} while (0);
inline static v8::Handle<v8::Value> NanThrowError(const char* errmsg) {
THROW_ERROR(v8::Exception::Error, errmsg);
_NAN_THROW_ERROR(v8::Exception::Error, errmsg);
}
inline static v8::Handle<v8::Value> NanThrowError(
@ -187,22 +286,43 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
}
inline static v8::Handle<v8::Value> NanThrowTypeError(const char* errmsg) {
THROW_ERROR(v8::Exception::TypeError, errmsg);
_NAN_THROW_ERROR(v8::Exception::TypeError, errmsg);
}
inline static v8::Handle<v8::Value> NanThrowRangeError(const char* errmsg) {
THROW_ERROR(v8::Exception::RangeError, errmsg);
_NAN_THROW_ERROR(v8::Exception::RangeError, errmsg);
}
static inline void NanDispose(v8::Persistent<v8::Object> &handle) {
template<class T> static inline void NanDispose(v8::Persistent<T> &handle) {
handle.Dispose();
}
static inline v8::Local<v8::Object> NanNewBufferHandle (
char *data,
size_t length,
node::Buffer::free_callback callback,
void *hint) {
return v8::Local<v8::Object>::New(node::Buffer::New(data, length, callback, hint)->handle_);
}
static inline v8::Local<v8::Object> NanNewBufferHandle (
char *data, uint32_t size) {
return v8::Local<v8::Object>::New(node::Buffer::New(data, size)->handle_);
}
static inline v8::Local<v8::Object> NanNewBufferHandle (uint32_t size) {
return v8::Local<v8::Object>::New(node::Buffer::New(size)->handle_);
}
static inline void FreeData(char *data, void *hint) {
delete[] data;
}
static inline v8::Local<v8::Object> NanBufferUse(char* data, uint32_t size) {
return v8::Local<v8::Object>::New(
node::Buffer::New(data, size, FreeData, NULL)->handle_);
}
template <class TypeName>
inline v8::Local<TypeName> NanPersistentToLocal(
const v8::Persistent<TypeName>& persistent) {
@ -220,6 +340,16 @@ static v8::Isolate* nan_isolate = v8::Isolate::GetCurrent();
return function_template->HasInstance(value);
}
static inline v8::Local<v8::Context> NanNewContextHandle(
v8::ExtensionConfiguration* extensions = NULL,
v8::Handle<v8::ObjectTemplate> g_template = v8::Handle<v8::ObjectTemplate>(),
v8::Handle<v8::Value> g_object = v8::Handle<v8::Value>()) {
v8::Persistent<v8::Context> ctx = v8::Context::New(extensions, g_template, g_object);
v8::Local<v8::Context> lctx = v8::Local<v8::Context>::New(ctx);
ctx.Dispose();
return lctx;
}
#endif // node version
class NanCallback {

Loading…
Cancel
Save