Browse Source

Merge branch 'pngstream'

v1.x
Tj Holowaychuk 14 years ago
parent
commit
7a5f93bb72
  1. 14
      lib/canvas.js
  2. 39
      lib/pngstream.js
  3. 44
      src/canvas.cc
  4. 1
      src/canvas.h
  5. 46
      test/canvas.test.js

14
lib/canvas.js

@ -14,7 +14,8 @@ var canvas = require('../build/default/canvas')
, Canvas = canvas.Canvas
, Context2d = canvas.CanvasRenderingContext2d
, CanvasGradient = canvas.CanvasGradient
, cairoVersion = canvas.cairoVersion;
, cairoVersion = canvas.cairoVersion
, PNGStream = require('./pngstream');
/**
* Export `Canvas` as the module.
@ -126,6 +127,17 @@ Canvas.prototype.getContext = function(contextId){
}
};
/**
* Create a `PNGStream` for `this` canvas.
*
* @return {PNGStream}
* @api public
*/
Canvas.prototype.createPNGStream = function(){
return new PNGStream(this);
};
/**
* Add `color` stop at the given `offset`.
*

39
lib/pngstream.js

@ -0,0 +1,39 @@
/*!
* Canvas - PNGStream
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter;
/**
* Initialize a `PNGStream` with the given `canvas`.
*
* @param {Canvas} canvas
* @api public
*/
var PNGStream = module.exports = function PNGStream(canvas) {
var self = this;
this.canvas = canvas;
process.nextTick(function(){
canvas.streamPNG(function(chunk, len){
if (len) {
self.emit('data', chunk, len);
} else {
self.emit('end');
}
});
});
};
/**
* Inherit from `EventEmitter`.
*/
PNGStream.prototype.__proto__ = EventEmitter.prototype;

44
src/canvas.cc

@ -6,8 +6,11 @@
//
#include "canvas.h"
#include <string.h>
#include <node_buffer.h>
using namespace v8;
using namespace node;
/*
* Initialize Canvas.
@ -20,6 +23,7 @@ Canvas::Initialize(Handle<Object> target) {
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("Canvas"));
NODE_SET_PROTOTYPE_METHOD(t, "streamPNG", StreamPNG);
NODE_SET_PROTOTYPE_METHOD(t, "savePNG", SavePNG);
target->Set(String::NewSymbol("Canvas"), t->GetFunction());
}
@ -42,6 +46,46 @@ Canvas::New(const Arguments &args) {
return args.This();
}
typedef struct {
Handle<Function> fn;
} closure_t;
static cairo_status_t
writeToBuffer(void *c, const uint8_t *data, unsigned len) {
closure_t *closure = (closure_t *) c;
Handle<Value> argv[2];
Buffer *buf = Buffer::New(len);
memcpy(buf->data(), data, len);
argv[0] = buf->handle_;
argv[1] = Integer::New(len);
closure->fn->Call(Context::GetCurrent()->Global(), 2, argv);
// TODO: CAIRO_STATUS_NO_MEMORY
// TODO: leak
return CAIRO_STATUS_SUCCESS;
}
/*
* Return a node Buffer containing PNG data.
*/
Handle<Value>
Canvas::StreamPNG(const Arguments &args) {
HandleScope scope;
// TODO: error handling
// TODO: nonblocking
if (!args[0]->IsFunction())
return ThrowException(Exception::TypeError(String::New("callback function required")));
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
closure_t closure;
closure.fn = Handle<Function>::Cast(args[0]);
cairo_surface_write_to_png_stream(canvas->getSurface(), writeToBuffer, &closure);
Handle<Value> argv[2];
argv[0] = Null();
argv[1] = Integer::New(0);
closure.fn->Call(Context::GetCurrent()->Global(), 2, argv);
return Undefined();
}
/*
* Initialize cairo surface.
*/

1
src/canvas.h

@ -38,6 +38,7 @@ class Canvas: public node::ObjectWrap {
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
static Handle<Value> SavePNG(const Arguments &args);
static Handle<Value> StreamPNG(const Arguments &args);
inline cairo_surface_t *getSurface(){ return _surface; }
Canvas(int width, int height);

46
test/canvas.test.js

@ -14,6 +14,10 @@ function hash(val) {
function assertChecksum(canvas, path, checksum, msg) {
canvas.savePNG(path);
assertChecksumOf(canvas, path, checksum, msg);
}
function assertChecksumOf(canvas, path, checksum, msg) {
fs.readFile(path, function(err, buf){
assert.equal(hash(buf), checksum, msg);
});
@ -499,5 +503,47 @@ module.exports = {
assert.ok(ctx.isPointInPath(60,110));
assert.ok(!ctx.isPointInPath(70,110));
assert.ok(!ctx.isPointInPath(50,120));
},
'test PNGStream': function(assert, beforeExit){
var canvas = new Canvas(320, 320)
, ctx = canvas.getContext('2d')
, path = __dirname + '/images/pngstream.png'
, called = 0;
ctx.strokeStyle = 'rgba(0,0,0,0.5)';
ctx.strokeRect(0,0,320,320);
ctx.fillStyle = 'rgba(0,0,0,0.02)';
var steps = 200;
while (steps--) {
ctx.fillRect(
160 - (steps / 2)
, 160 - (steps / 2)
, steps
, steps
);
}
var out = fs.createWriteStream(path)
, stream = canvas.createPNGStream();
out.on('close', function(){
assertChecksumOf(
canvas
, path
, '04f2e1b4338de2d7451194cba7d29970'
, 'PNGStream failed');
});
stream.on('data', function(chunk){ out.write(chunk); });
stream.on('end', function(){
++called;
out.end();
});
beforeExit(function(){
assert.equal(1, called);
});
}
}
Loading…
Cancel
Save