Browse Source

Merge branch 'Image'

v1.x
Tj Holowaychuk 15 years ago
parent
commit
3f44331114
  1. 1
      index.js
  2. 2
      lib/canvas.js
  3. 267
      src/Image.cc
  4. 46
      src/Image.h
  5. 2
      src/init.cc
  6. BIN
      test/fixtures/clock.png
  7. 58
      test/image.test.js

1
index.js

@ -0,0 +1 @@
module.exports = require('./lib/canvas');

2
lib/canvas.js

@ -11,6 +11,7 @@
var canvas = require('../build/default/canvas')
, Canvas = canvas.Canvas
, Image = canvas.Image
, cairoVersion = canvas.cairoVersion
, Context2d = require('./context2d')
, PNGStream = require('./pngstream')
@ -40,6 +41,7 @@ exports.cairoVersion = cairoVersion;
exports.Context2d = Context2d;
exports.PNGStream = PNGStream;
exports.Image = Image;
/**
* Context2d implementation.

267
src/Image.cc

@ -0,0 +1,267 @@
//
// Image.cc
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#include "Canvas.h"
#include "Image.h"
/*
* Initialize Image.
*/
void
Image::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(Image::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("Image"));
Local<ObjectTemplate> proto = t->PrototypeTemplate();
NODE_SET_PROTOTYPE_METHOD(t, "inspect", Inspect);
proto->SetAccessor(String::NewSymbol("src"), GetSrc, SetSrc);
proto->SetAccessor(String::NewSymbol("complete"), GetComplete);
proto->SetAccessor(String::NewSymbol("width"), GetWidth);
proto->SetAccessor(String::NewSymbol("height"), GetHeight);
proto->SetAccessor(String::NewSymbol("onload"), GetOnload, SetOnload);
proto->SetAccessor(String::NewSymbol("onerror"), GetOnerror, SetOnerror);
target->Set(String::NewSymbol("Image"), t->GetFunction());
}
/*
* Initialize a new Image.
*/
Handle<Value>
Image::New(const Arguments &args) {
HandleScope scope;
Image *img = new Image;
img->Wrap(args.This());
return args.This();
}
/*
* Custom inspect.
*/
Handle<Value>
Image::Inspect(const Arguments &args) {
HandleScope scope;
Image *img = ObjectWrap::Unwrap<Image>(args.This());
Local<String> str = String::New("[Image");
if (img->filename) {
str = String::Concat(str, String::New(" "));
str = String::Concat(str, String::New(img->filename));
}
str = String::Concat(str, String::New("]"));
return scope.Close(str);
}
/*
* Get complete boolean.
*/
Handle<Value>
Image::GetComplete(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return Boolean::New(img->complete);
}
/*
* Get width.
*/
Handle<Value>
Image::GetWidth(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return Number::New(img->width);
}
/*
* Get height.
*/
Handle<Value>
Image::GetHeight(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return Number::New(img->height);
}
/*
* Get src path.
*/
Handle<Value>
Image::GetSrc(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return String::New(img->filename);
}
/*
* Set src path.
*/
void
Image::SetSrc(Local<String>, Local<Value> val, const AccessorInfo &info) {
if (val->IsString()) {
String::AsciiValue src(val);
Image *img = ObjectWrap::Unwrap<Image>(info.This());
img->filename = *src;
img->load();
}
}
/*
* Get onload callback.
*/
Handle<Value>
Image::GetOnload(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return img->onload;
}
/*
* Set onload callback.
*/
void
Image::SetOnload(Local<String>, Local<Value> val, const AccessorInfo &info) {
if (val->IsFunction()) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
img->onload = Persistent<Function>::New(Handle<Function>::Cast(val));
}
}
/*
* Get onerror callback.
*/
Handle<Value>
Image::GetOnerror(Local<String>, const AccessorInfo &info) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
return img->onerror;
}
/*
* Set onerror callback.
*/
void
Image::SetOnerror(Local<String>, Local<Value> val, const AccessorInfo &info) {
if (val->IsFunction()) {
Image *img = ObjectWrap::Unwrap<Image>(info.This());
img->onerror = Persistent<Function>::New(Handle<Function>::Cast(val));
}
}
/*
* Initialize a new Image.
*/
Image::Image() {
complete = false;
filename = NULL;
_surface = NULL;
}
/*
* Destroy image and associated surface.
*/
Image::~Image() {
if (_surface) cairo_surface_destroy(_surface);
}
/*
* Load callback.
*/
static int
EIO_Load(eio_req *req) {
Image *img = (Image *) req->data;
req->result = img->loadSurface();
return 0;
}
/*
* After load callback.
*/
static int
EIO_AfterLoad(eio_req *req) {
HandleScope scope;
Image *img = (Image *) req->data;
if (req->result) {
img->error(Canvas::Error((cairo_status_t) req->result));
} else {
img->loaded();
}
ev_unref(EV_DEFAULT_UC);
return 0;
}
/*
* Initiate image loading.
*/
void
Image::load() {
Ref();
complete = false;
eio_custom(EIO_Load, EIO_PRI_DEFAULT, EIO_AfterLoad, this);
ev_ref(EV_DEFAULT_UC);
}
/*
* Invoke onload (when assigned) and assign dimensions.
*/
void
Image::loaded() {
HandleScope scope;
complete = true;
if (!onload.IsEmpty()) {
TryCatch try_catch;
onload->Call(Context::GetCurrent()->Global(), 0, NULL);
onload.Dispose();
if (try_catch.HasCaught()) {
error(try_catch.Exception());
}
}
Unref();
}
/*
* Invoke onerror (when assigned) with the given err.
*/
void
Image::error(Local<Value> err) {
HandleScope scope;
// TODO: handle exception in onerror segfault
if (!onerror.IsEmpty()) {
Local<Value> argv[1] = { err };
onerror->Call(Context::GetCurrent()->Global(), 1, argv);
}
}
/*
* Load cairo surface from the image src.
*
* TODO: support more formats
*/
cairo_status_t
Image::loadSurface() {
_surface = cairo_image_surface_create_from_png(filename);
width = cairo_image_surface_get_width(_surface);
height = cairo_image_surface_get_height(_surface);
return cairo_surface_status(_surface);
}

46
src/Image.h

@ -0,0 +1,46 @@
//
// Image.h
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#ifndef __NODE_IMAGE_H__
#define __NODE_IMAGE_H__
#include "canvas.h"
using namespace v8;
class Image: public node::ObjectWrap {
public:
bool complete;
char *filename;
int width, height;
Persistent<Function> onload;
Persistent<Function> onerror;
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
static Handle<Value> Inspect(const Arguments &args);
static Handle<Value> GetSrc(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetOnload(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetOnerror(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetComplete(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetWidth(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetHeight(Local<String> prop, const AccessorInfo &info);
static void SetSrc(Local<String> prop, Local<Value> val, const AccessorInfo &info);
static void SetOnload(Local<String> prop, Local<Value> val, const AccessorInfo &info);
static void SetOnerror(Local<String> prop, Local<Value> val, const AccessorInfo &info);
inline cairo_surface_t *surface(){ return _surface; }
cairo_status_t loadSurface();
void error(Local<Value>);
void loaded();
void load();
Image();
private:
cairo_surface_t *_surface;
~Image();
};
#endif

2
src/init.cc

@ -6,6 +6,7 @@
//
#include "Canvas.h"
#include "Image.h"
#include "CanvasGradient.h"
#include "CanvasRenderingContext2d.h"
@ -13,6 +14,7 @@ extern "C" void
init (Handle<Object> target) {
HandleScope scope;
Canvas::Initialize(target);
Image::Initialize(target);
Context2d::Initialize(target);
Gradient::Initialize(target);
target->Set(String::New("cairoVersion"), String::New(cairo_version_string()));

BIN
test/fixtures/clock.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

58
test/image.test.js

@ -0,0 +1,58 @@
/**
* Module dependencies.
*/
var Canvas = require('canvas')
, Image = Canvas.Image;
var png = __dirname + '/fixtures/clock.png';
module.exports = {
'tset Image': function(assert){
assert.ok(Image instanceof Function);
},
'test Image#onload': function(assert, beforeExit){
var img = new Image
, n = 0;
assert.strictEqual(false, img.complete);
img.onload = function(){
++n;
};
img.src = png;
assert.equal(img.src, png);
beforeExit(function(){
assert.strictEqual(true, img.complete);
assert.strictEqual(320, img.width);
assert.strictEqual(320, img.height);
assert.equal(1, n);
});
},
'test Image#onerror': function(assert, beforeExit){
var img = new Image
, n = 0;
assert.strictEqual(false, img.complete);
img.onload = function(){
assert.fail('called onload');
};
img.onerror = function(err){
++n;
assert.strictEqual(false, img.complete);
assert.ok(err instanceof Error, 'did not invoke onerror() with error');
};
img.src = png + 's';
assert.equal(img.src, png + 's');
beforeExit(function(){
assert.equal(1, n);
});
}
};
Loading…
Cancel
Save