Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Started on ctx.createPattern() support.

  • Loading branch information...
commit 559bd9d572d871322ca4f2a643d2aa5f605d2cbd 1 parent 6cd403e
Robert Sköld slaskis authored
15 lib/context2d.js
View
@@ -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
88 src/CanvasPattern.cc
View
@@ -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
View
@@ -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
View
@@ -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
View
@@ -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
View
@@ -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
View
@@ -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
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Please sign in to comment.
Something went wrong with that request. Please try again.