Browse files

Started on ctx.createPattern() support.

  • Loading branch information...
1 parent 6cd403e commit 559bd9d572d871322ca4f2a643d2aa5f605d2cbd @slaskis slaskis committed Jan 17, 2012
View
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;
@@ -99,6 +100,20 @@ var parseFont = exports.parseFont = function(str){
};
/**
+ * 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)`.
*
* @param {Number} x0
View
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);
+}
View
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
View
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();
}
View
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;
View
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()));
}
View
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]);
+
+ },
}
View
BIN test/fixtures/checkers.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 559bd9d

Please sign in to comment.