Browse Source

Merge branch 'gradients'

v1.x
Tj Holowaychuk 14 years ago
parent
commit
5c7c7c4921
  1. 2
      Readme.md
  2. 29
      examples/gradients.js
  3. 55
      lib/canvas.js
  4. 2
      src/canvas.cc
  5. 20
      src/canvas.h
  6. 73
      src/context2d.cc
  7. 6
      src/context2d.h
  8. 78
      src/gradient.cc
  9. 29
      src/gradient.h
  10. 2
      src/node-canvas.cc

2
Readme.md

@ -14,9 +14,9 @@
[x] list of valid css colors
[x] match canvas defaults (mostly, test suite from webkit would be nice)
[x] transformations
[x] gradients
[ ] make sure constructor names match
[ ] anti aliasing
[ ] gradients
[ ] Image
[ ] patterns
[ ] text

29
examples/gradients.js

@ -0,0 +1,29 @@
/**
* Module dependencies.
*/
var Canvas = require('../lib/canvas')
, canvas = new Canvas(320, 320)
, ctx = canvas.getContext('2d');
// Create gradients
var lingrad = ctx.createLinearGradient(0,0,0,150);
lingrad.addColorStop(0, '#00ABEB');
lingrad.addColorStop(0.5, '#fff');
lingrad.addColorStop(0.5, '#26C000');
lingrad.addColorStop(1, '#fff');
var lingrad2 = ctx.createLinearGradient(0,50,0,95);
lingrad2.addColorStop(0.5, '#000');
lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
// assign gradients to fill and stroke styles
ctx.fillStyle = lingrad;
ctx.strokeStyle = lingrad2;
// draw shapes
ctx.fillRect(10,10,130,130);
ctx.strokeRect(50,50,50,50);
canvas.savePNG(__dirname + '/gradients.png');

55
lib/canvas.js

@ -13,6 +13,7 @@ var canvas = require('../build/default/canvas')
, colors = require('./colors')
, Canvas = canvas.Canvas
, Context2d = canvas.Context2d
, CanvasGradient = canvas.CanvasGradient
, cairoVersion = canvas.cairoVersion;
/**
@ -118,6 +119,24 @@ Canvas.prototype.getContext = function(contextId){
}
};
CanvasGradient.prototype.addColorStop = function(offset, color){
var rgba = exports.parseColor(color) || [0,0,0,1];
this.addColorStopRGBA(
offset
, rgba[0]
, rgba[1]
, rgba[2]
, rgba[3]);
};
Context2d.prototype.createLinearGradient = function(x0, y0, x1, y1){
return new CanvasGradient(x0, y0, x1, y1);
};
Context2d.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){
return new CanvasGradient(x0, y0, r0, x1, y1, r1);
};
/**
* Set the fill style with the given css color string.
*
@ -126,13 +145,17 @@ Canvas.prototype.getContext = function(contextId){
*/
Context2d.prototype.__defineSetter__('fillStyle', function(val){
var rgba = exports.parseColor(val) || [0,0,0,1];
this.lastFillStyle = rgba;
this.setFillRGBA(
rgba[0]
, rgba[1]
, rgba[2]
, rgba[3]);
if (val instanceof CanvasGradient) {
this.setFillPattern(val);
} else if ('string' == typeof val) {
var rgba = exports.parseColor(val) || [0,0,0,1];
this.lastFillStyle = rgba;
this.setFillRGBA(
rgba[0]
, rgba[1]
, rgba[2]
, rgba[3]);
}
});
/**
@ -154,13 +177,17 @@ Context2d.prototype.__defineGetter__('fillStyle', function(){
*/
Context2d.prototype.__defineSetter__('strokeStyle', function(val){
var rgba = exports.parseColor(val) || [0,0,0,1];
this.lastStrokeStyle = rgba;
this.setStrokeRGBA(
rgba[0]
, rgba[1]
, rgba[2]
, rgba[3]);
if (val instanceof CanvasGradient) {
this.setStrokePattern(val);
} else if ('string' == typeof val) {
var rgba = exports.parseColor(val) || [0,0,0,1];
this.lastStrokeStyle = rgba;
this.setStrokeRGBA(
rgba[0]
, rgba[1]
, rgba[2]
, rgba[3]);
}
});
/**

2
src/canvas.cc

@ -37,7 +37,7 @@ Canvas::New(const Arguments &args) {
if (!args[1]->IsNumber())
return ThrowException(Exception::TypeError(String::New("height required")));
Canvas *canvas = new Canvas(args[0]->Uint32Value(), args[1]->Uint32Value());
Canvas *canvas = new Canvas(args[0]->Int32Value(), args[1]->Int32Value());
canvas->Wrap(args.This());
return args.This();
}

20
src/canvas.h

@ -13,6 +13,24 @@
#include <node_object_wrap.h>
#include <cairo.h>
/*
* RGBA arg assertions.
*/
#define RGBA_ARGS(N) \
if (!args[N]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("r required"))); \
if (!args[N+1]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("g required"))); \
if (!args[N+2]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("b required"))); \
if (!args[N+3]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("alpha required"))); \
float r = args[N]->Int32Value(); \
float g = args[N+1]->Int32Value(); \
float b = args[N+2]->Int32Value(); \
float a = args[N+3]->NumberValue();
using namespace v8;
class Canvas: public node::ObjectWrap {
@ -21,8 +39,6 @@ class Canvas: public node::ObjectWrap {
static Handle<Value> New(const Arguments &args);
static Handle<Value> SavePNG(const Arguments &args);
inline cairo_surface_t *getSurface(){ return _surface; }
protected:
Canvas(int width, int height);
private:

73
src/context2d.cc

@ -5,10 +5,11 @@
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#include "canvas.h"
#include "context2d.h"
#include <math.h>
#include <string.h>
#include "canvas.h"
#include "context2d.h"
#include "gradient.h"
using namespace v8;
using namespace node;
@ -23,6 +24,12 @@ using namespace node;
_.b = B / 255 * 1; \
_.a = A; \
#define SET_SOURCE(C) \
if (C##Pattern) \
cairo_set_source(ctx, C##Pattern); \
else \
SET_SOURCE_RGBA(C)
/*
* Set source RGBA.
*/
@ -48,24 +55,6 @@ using namespace node;
int width = args[2]->Int32Value(); \
int height = args[3]->Int32Value();
/*
* RGBA arg assertions.
*/
#define RGBA_ARGS \
if (!args[0]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("r required"))); \
if (!args[1]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("g required"))); \
if (!args[2]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("b required"))); \
if (!args[3]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("alpha required"))); \
float r = args[0]->Int32Value(); \
float g = args[1]->Int32Value(); \
float b = args[2]->Int32Value(); \
float a = args[3]->NumberValue();
/*
* Initialize Context2d.
*/
@ -100,6 +89,8 @@ Context2d::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "arc", Arc);
NODE_SET_PROTOTYPE_METHOD(t, "setFillRGBA", SetFillRGBA);
NODE_SET_PROTOTYPE_METHOD(t, "setStrokeRGBA", SetStrokeRGBA);
NODE_SET_PROTOTYPE_METHOD(t, "setFillPattern", SetFillPattern);
NODE_SET_PROTOTYPE_METHOD(t, "setStrokePattern", SetStrokePattern);
proto->SetAccessor(String::NewSymbol("globalCompositeOperation"), GetGlobalCompositeOperation, SetGlobalCompositeOperation);
proto->SetAccessor(String::NewSymbol("globalAlpha"), GetGlobalAlpha, SetGlobalAlpha);
proto->SetAccessor(String::NewSymbol("miterLimit"), GetMiterLimit, SetMiterLimit);
@ -130,6 +121,7 @@ Context2d::Context2d(Canvas *canvas): ObjectWrap() {
_canvas = canvas;
_context = cairo_create(canvas->getSurface());
cairo_set_line_width(_context, 1);
fillPattern = strokePattern = NULL;
globalAlpha = -1;
RGBA(fill,0,0,0,1);
RGBA(stroke,0,0,0,1);
@ -337,6 +329,34 @@ Context2d::SetLineCap(Local<String> prop, Local<Value> val, const AccessorInfo &
}
}
/*
* Set fill pattern, used internally for fillStyle=
*/
Handle<Value>
Context2d::SetFillPattern(const Arguments &args) {
HandleScope scope;
// TODO: HasInstance / error handling
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(args[0]->ToObject());
context->fillPattern = grad->getPattern();
return Undefined();
}
/*
* Set stroke pattern, used internally for strokeStyle=
*/
Handle<Value>
Context2d::SetStrokePattern(const Arguments &args) {
HandleScope scope;
// TODO: HasInstance / error handling
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(args[0]->ToObject());
context->strokePattern = grad->getPattern();
return Undefined();
}
/*
* Set fill RGBA, used internally for fillStyle=
*/
@ -344,7 +364,7 @@ Context2d::SetLineCap(Local<String> prop, Local<Value> val, const AccessorInfo &
Handle<Value>
Context2d::SetFillRGBA(const Arguments &args) {
HandleScope scope;
RGBA_ARGS;
RGBA_ARGS(0);
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
RGBA(context->fill,r,g,b,a);
return Undefined();
@ -357,7 +377,7 @@ Context2d::SetFillRGBA(const Arguments &args) {
Handle<Value>
Context2d::SetStrokeRGBA(const Arguments &args) {
HandleScope scope;
RGBA_ARGS;
RGBA_ARGS(0);
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
RGBA(context->stroke,r,g,b,a);
return Undefined();
@ -511,7 +531,7 @@ Context2d::Fill(const Arguments &args) {
HandleScope scope;
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
SET_SOURCE_RGBA(context->fill);
SET_SOURCE(context->fill);
cairo_fill_preserve(ctx);
return Undefined();
}
@ -525,7 +545,7 @@ Context2d::Stroke(const Arguments &args) {
HandleScope scope;
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
SET_SOURCE_RGBA(context->stroke);
SET_SOURCE(context->stroke);
cairo_stroke_preserve(ctx);
return Undefined();
}
@ -583,7 +603,7 @@ Context2d::FillRect(const Arguments &args) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
cairo_rectangle(ctx, x, y, width, height);
SET_SOURCE_RGBA(context->fill);
SET_SOURCE(context->fill);
cairo_fill(ctx);
return Undefined();
}
@ -599,7 +619,7 @@ Context2d::StrokeRect(const Arguments &args) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
cairo_rectangle(ctx, x, y, width, height);
SET_SOURCE_RGBA(context->stroke);
SET_SOURCE(context->stroke);
cairo_stroke(ctx);
return Undefined();
}
@ -616,7 +636,6 @@ Context2d::ClearRect(const Arguments &args) {
cairo_t *ctx = context->getContext();
cairo_set_operator(ctx, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(ctx, x, y, width, height);
SET_SOURCE_RGBA(context->fill);
cairo_fill(ctx);
cairo_set_operator(ctx, CAIRO_OPERATOR_OVER);
return Undefined();

6
src/context2d.h

@ -9,6 +9,7 @@
#define __NODE_CONTEXT2D_H__
#include "canvas.h"
#include "gradient.h"
typedef struct {
float r, g, b, a;
@ -18,6 +19,8 @@ class Context2d: public node::ObjectWrap {
public:
rgba_t fill;
rgba_t stroke;
cairo_pattern_t *fillPattern;
cairo_pattern_t *strokePattern;
float globalAlpha;
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
@ -31,8 +34,11 @@ class Context2d: public node::ObjectWrap {
static Handle<Value> ClosePath(const Arguments &args);
static Handle<Value> Fill(const Arguments &args);
static Handle<Value> Stroke(const Arguments &args);
static Handle<Value> SetSource(const Arguments &args);
static Handle<Value> SetFillRGBA(const Arguments &args);
static Handle<Value> SetStrokeRGBA(const Arguments &args);
static Handle<Value> SetFillPattern(const Arguments &args);
static Handle<Value> SetStrokePattern(const Arguments &args);
static Handle<Value> BezierCurveTo(const Arguments &args);
static Handle<Value> LineTo(const Arguments &args);
static Handle<Value> MoveTo(const Arguments &args);

78
src/gradient.cc

@ -0,0 +1,78 @@
//
// gradient.cc
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#include "canvas.h"
#include "gradient.h"
void
Gradient::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(Gradient::New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("CanvasGradient"));
NODE_SET_PROTOTYPE_METHOD(t, "addColorStopRGBA", AddColorStopRGBA);
target->Set(String::NewSymbol("CanvasGradient"), t->GetFunction());
}
Handle<Value>
Gradient::New(const Arguments &args) {
HandleScope scope;
// Linear
if (4 == args.Length()) {
Gradient *grad = new Gradient(
args[0]->NumberValue()
, args[1]->NumberValue()
, args[2]->NumberValue()
, args[3]->NumberValue());
grad->Wrap(args.This());
return args.This();
}
// Radial
if (6 == args.Length()) {
Gradient *grad = new Gradient(
args[0]->NumberValue()
, args[1]->NumberValue()
, args[2]->NumberValue()
, args[3]->NumberValue()
, args[4]->NumberValue()
, args[5]->NumberValue());
grad->Wrap(args.This());
return args.This();
}
return ThrowException(Exception::TypeError(String::New("invalid arguments")));
}
Handle<Value>
Gradient::AddColorStopRGBA(const Arguments &args) {
HandleScope scope;
if (!args[0]->IsNumber()) \
return ThrowException(Exception::TypeError(String::New("offset required"))); \
RGBA_ARGS(1);
Gradient *grad = ObjectWrap::Unwrap<Gradient>(args.This());
cairo_pattern_add_color_stop_rgba(
grad->getPattern()
, args[0]->NumberValue()
, r / 255 * 1
, g / 255 * 1
, b / 255 * 1
, a);
return Undefined();
}
Gradient::Gradient(double x0, double y0, double x1, double y1):
_x0(x0), _y0(y0), _x1(x1), _y1(y1) {
_pattern = cairo_pattern_create_linear(x0, y0, x1, y1);
}
Gradient::Gradient(double x0, double y0, double r0, double x1, double y1, double r1):
_x0(x0), _y0(y0), _x1(x1), _y1(y1), _r0(r0), _r1(r1) {
_pattern = cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1);
}

29
src/gradient.h

@ -0,0 +1,29 @@
//
// gradient.h
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#ifndef __NODE_GRADIENT_H__
#define __NODE_GRADIENT_H__
#include "canvas.h"
using namespace v8;
class Gradient: public node::ObjectWrap {
public:
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
static Handle<Value> AddColorStopRGBA(const Arguments &args);
Gradient(double x0, double y0, double x1, double y1);
Gradient(double x0, double y0, double r0, double x1, double y1, double r1);
inline cairo_pattern_t *getPattern(){ return _pattern; }
private:
double _x0, _y0, _x1, _y1, _r0, _r1;
cairo_pattern_t *_pattern;
};
#endif

2
src/node-canvas.cc

@ -7,11 +7,13 @@
#include "canvas.h"
#include "context2d.h"
#include "gradient.h"
extern "C" void
init (Handle<Object> target) {
HandleScope scope;
Canvas::Initialize(target);
Context2d::Initialize(target);
Gradient::Initialize(target);
target->Set(String::New("cairoVersion"), String::New(cairo_version_string()));
}
Loading…
Cancel
Save