Browse Source

Started on ctx.createPattern() support.

v1.x
Robert Sköld 13 years ago
parent
commit
559bd9d572
  1. 15
      lib/context2d.js
  2. 88
      src/CanvasPattern.cc
  3. 28
      src/CanvasPattern.h
  4. 54
      src/CanvasRenderingContext2d.cc
  5. 2
      src/CanvasRenderingContext2d.h
  6. 2
      src/init.cc
  7. 82
      test/canvas.test.js
  8. BIN
      test/fixtures/checkers.png

15
lib/context2d.js

@ -12,6 +12,7 @@
var canvas = require('./bindings')
, Context2d = canvas.CanvasRenderingContext2d
, CanvasGradient = canvas.CanvasGradient
, CanvasPattern = canvas.CanvasPattern
, ImageData = canvas.ImageData
, PixelArray = canvas.CanvasPixelArray;
@ -98,6 +99,20 @@ var parseFont = exports.parseFont = function(str){
return cache[str] = font;
};
/**
* Create a pattern from `Image` or `Canvas`.
*
* @param {Image|Canvas} image
* @param {String} repetition
* @return {CanvasPattern}
* @api public
*/
Context2d.prototype.createPattern = function(image, repetition){
// TODO Use repetition (currently always 'repeat')
return new CanvasPattern(image);
};
/**
* Create a linear gradient at the given point `(x0, y0)` and `(x1, y1)`.
*

88
src/CanvasPattern.cc

@ -0,0 +1,88 @@
//
// Pattern.cc
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#include "Canvas.h"
#include "Image.h"
#include "CanvasPattern.h"
Persistent<FunctionTemplate> Pattern::constructor;
/*
* Initialize CanvasPattern.
*/
void
Pattern::Initialize(Handle<Object> target) {
HandleScope scope;
// Constructor
constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(Pattern::New));
constructor->InstanceTemplate()->SetInternalFieldCount(1);
constructor->SetClassName(String::NewSymbol("CanvasPattern"));
// Prototype
target->Set(String::NewSymbol("CanvasPattern"), constructor->GetFunction());
}
/*
* Initialize a new CanvasPattern.
*/
Handle<Value>
Pattern::New(const Arguments &args) {
HandleScope scope;
int w = 0
, h = 0;
cairo_surface_t *surface;
Local<Object> obj = args[0]->ToObject();
// Image
if (Image::constructor->HasInstance(obj)) {
Image *img = ObjectWrap::Unwrap<Image>(obj);
if (!img->isComplete()) {
return ThrowException(Exception::Error(String::New("Image given has not completed loading")));
}
w = img->width;
h = img->height;
surface = img->surface();
// Canvas
} else if (Canvas::constructor->HasInstance(obj)) {
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(obj);
w = canvas->width;
h = canvas->height;
surface = canvas->surface();
// Invalid
} else {
return ThrowException(Exception::TypeError(String::New("Image or Canvas expected")));
}
Pattern *pattern = new Pattern(surface,w,h);
pattern->Wrap(args.This());
return args.This();
}
/*
* Initialize linear gradient.
*/
Pattern::Pattern(cairo_surface_t *surface, int w, int h):
_width(w), _height(h) {
_pattern = cairo_pattern_create_for_surface(surface);
}
/*
* Destroy the pattern.
*/
Pattern::~Pattern() {
cairo_pattern_destroy(_pattern);
}

28
src/CanvasPattern.h

@ -0,0 +1,28 @@
//
// CanvasPattern.h
//
// Copyright (c) 2011 LearnBoost <tj@learnboost.com>
//
#ifndef __NODE_PATTERN_H__
#define __NODE_PATTERN_H__
#include "Canvas.h"
class Pattern: public node::ObjectWrap {
public:
static Persistent<FunctionTemplate> constructor;
static void Initialize(Handle<Object> target);
static Handle<Value> New(const Arguments &args);
Pattern(cairo_surface_t *surface, int w, int h);
inline cairo_pattern_t *pattern(){ return _pattern; }
private:
~Pattern();
int _width, _height;
// TODO REPEAT/REPEAT_X/REPEAT_Y
cairo_pattern_t *_pattern;
};
#endif

54
src/CanvasRenderingContext2d.cc

@ -14,6 +14,7 @@
#include "ImageData.h"
#include "CanvasRenderingContext2d.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
Persistent<FunctionTemplate> Context2d::constructor;
@ -125,6 +126,7 @@ Context2d::Context2d(Canvas *canvas) {
state->globalAlpha = 1;
state->textAlignment = -1;
state->fillPattern = state->strokePattern = NULL;
state->strokeGradient = state->strokeGradient = NULL;
state->textBaseline = NULL;
rgba_t transparent = { 0,0,0,1 };
rgba_t transparent_black = { 0,0,0,0 };
@ -218,8 +220,12 @@ Context2d::restorePath() {
void
Context2d::fill(bool preserve) {
if (state->fillPattern) {
cairo_pattern_set_filter(state->fillPattern, state->patternQuality);
cairo_set_source(_context, state->fillPattern);
cairo_set_source(_context, state->fillPattern);
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
// TODO repeat/repeat-x/repeat-y
} else if (state->fillGradient) {
cairo_pattern_set_filter(state->fillGradient, state->patternQuality);
cairo_set_source(_context, state->fillGradient);
} else {
setSourceRGBA(state->fill);
}
@ -242,8 +248,11 @@ Context2d::fill(bool preserve) {
void
Context2d::stroke(bool preserve) {
if (state->strokePattern) {
cairo_pattern_set_filter(state->strokePattern, state->patternQuality);
cairo_set_source(_context, state->fillPattern);
cairo_set_source(_context, state->strokePattern);
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
} else if (state->strokeGradient) {
cairo_pattern_set_filter(state->strokeGradient, state->patternQuality);
cairo_set_source(_context, state->strokeGradient);
} else {
setSourceRGBA(state->stroke);
}
@ -1052,12 +1061,17 @@ Context2d::SetFillPattern(const Arguments &args) {
HandleScope scope;
Local<Object> obj = args[0]->ToObject();
if (!Gradient::constructor->HasInstance(obj))
return ThrowException(Exception::TypeError(String::New("Gradient expected")));
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(obj);
context->state->fillPattern = grad->pattern();
if (Gradient::constructor->HasInstance(obj)){
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(obj);
context->state->strokeGradient = grad->pattern();
} else if(Pattern::constructor->HasInstance(obj)){
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Pattern *pattern = ObjectWrap::Unwrap<Pattern>(obj);
context->state->fillPattern = pattern->pattern();
} else {
return ThrowException(Exception::TypeError(String::New("Gradient or Pattern expected")));
}
return Undefined();
}
@ -1070,12 +1084,18 @@ Context2d::SetStrokePattern(const Arguments &args) {
HandleScope scope;
Local<Object> obj = args[0]->ToObject();
if (!Gradient::constructor->HasInstance(obj))
return ThrowException(Exception::TypeError(String::New("Gradient expected")));
if (Gradient::constructor->HasInstance(obj)){
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(obj);
context->state->strokeGradient = grad->pattern();
} else if(Pattern::constructor->HasInstance(obj)){
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Pattern *pattern = ObjectWrap::Unwrap<Pattern>(obj);
context->state->strokePattern = pattern->pattern();
} else {
return ThrowException(Exception::TypeError(String::New("Gradient or Pattern expected")));
}
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
Gradient *grad = ObjectWrap::Unwrap<Gradient>(obj);
context->state->strokePattern = grad->pattern();
return Undefined();
}
@ -1120,7 +1140,7 @@ Context2d::SetFillColor(const Arguments &args) {
uint32_t rgba = rgba_from_string(*str, &ok);
if (!ok) return Undefined();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
context->state->fillPattern = NULL;
context->state->fillPattern = context->state->fillGradient = NULL;
context->state->fill = rgba_create(rgba);
return Undefined();
}
@ -1151,7 +1171,7 @@ Context2d::SetStrokeColor(const Arguments &args) {
uint32_t rgba = rgba_from_string(*str, &ok);
if (!ok) return Undefined();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
context->state->strokePattern = NULL;
context->state->strokePattern = context->state->strokeGradient = NULL;
context->state->stroke = rgba_create(rgba);
return Undefined();
}

2
src/CanvasRenderingContext2d.h

@ -25,6 +25,8 @@ typedef struct {
cairo_filter_t patternQuality;
cairo_pattern_t *fillPattern;
cairo_pattern_t *strokePattern;
cairo_pattern_t *fillGradient;
cairo_pattern_t *strokeGradient;
float globalAlpha;
short textAlignment;
short textBaseline;

2
src/init.cc

@ -10,6 +10,7 @@
#include "ImageData.h"
#include "PixelArray.h"
#include "CanvasGradient.h"
#include "CanvasPattern.h"
#include "CanvasRenderingContext2d.h"
extern "C" void
@ -21,5 +22,6 @@ init (Handle<Object> target) {
PixelArray::Initialize(target);
Context2d::Initialize(target);
Gradient::Initialize(target);
Pattern::Initialize(target);
target->Set(String::New("cairoVersion"), String::New(cairo_version_string()));
}

82
test/canvas.test.js

@ -436,5 +436,85 @@ module.exports = {
assert.equal(255, data[0]);
data[0] = -4444;
assert.equal(0, data[0]);
}
},
'test Context2d#createPattern(Canvas)': function(){
var pattern = new Canvas(2,2)
, checkers = pattern.getContext('2d');
// white
checkers.fillStyle = '#fff';
checkers.fillRect(0,0,2,2);
// black
checkers.fillStyle = '#000';
checkers.fillRect(0,0,1,1);
checkers.fillRect(1,1,1,1);
var imageData = checkers.getImageData(0,0,2,2);
assert.equal(2, imageData.width);
assert.equal(2, imageData.height);
assert.equal(16, imageData.data.length);
// (0,0) black
assert.equal(0, imageData.data[0]);
assert.equal(0, imageData.data[1]);
assert.equal(0, imageData.data[2]);
assert.equal(255, imageData.data[3]);
// (1,0) white
assert.equal(255, imageData.data[4]);
assert.equal(255, imageData.data[5]);
assert.equal(255, imageData.data[6]);
assert.equal(255, imageData.data[7]);
// (0,1) white
assert.equal(255, imageData.data[8]);
assert.equal(255, imageData.data[9]);
assert.equal(255, imageData.data[10]);
assert.equal(255, imageData.data[11]);
// (1,1) black
assert.equal(0, imageData.data[12]);
assert.equal(0, imageData.data[13]);
assert.equal(0, imageData.data[14]);
assert.equal(255, imageData.data[15]);
var canvas = new Canvas(20, 20)
, ctx = canvas.getContext('2d')
, pattern = ctx.createPattern(pattern);
ctx.fillStyle = pattern;
ctx.fillRect(0,0,20,20);
var imageData = ctx.getImageData(0,0,20,20);
assert.equal(20, imageData.width);
assert.equal(20, imageData.height);
assert.equal(1600, imageData.data.length);
// (0,0) black
assert.equal(0, imageData.data[0]);
assert.equal(0, imageData.data[1]);
assert.equal(0, imageData.data[2]);
assert.equal(255, imageData.data[3]);
// (1,0) white
assert.equal(255, imageData.data[4]);
assert.equal(255, imageData.data[5]);
assert.equal(255, imageData.data[6]);
assert.equal(255, imageData.data[7]);
// (2,0) black
assert.equal(0, imageData.data[8]);
assert.equal(0, imageData.data[9]);
assert.equal(0, imageData.data[10]);
assert.equal(255, imageData.data[11]);
// (3,0) white
assert.equal(255, imageData.data[12]);
assert.equal(255, imageData.data[13]);
assert.equal(255, imageData.data[14]);
assert.equal(255, imageData.data[15]);
},
}

BIN
test/fixtures/checkers.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Loading…
Cancel
Save