Skip to content

Commit

Permalink
Add support for pattern repeat and no-repeat (#1066)
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored and zbjornson committed Dec 27, 2017
1 parent a84b2bc commit 08a70e2
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 13 deletions.
3 changes: 1 addition & 2 deletions lib/context2d.js
Expand Up @@ -67,8 +67,7 @@ Context2d.prototype.__defineGetter__('imageSmoothingEnabled', function(val){
*/

Context2d.prototype.createPattern = function(image, repetition){
// TODO Use repetition (currently always 'repeat')
return new CanvasPattern(image);
return new CanvasPattern(image, repetition || 'repeat');
};

/**
Expand Down
24 changes: 19 additions & 5 deletions src/CanvasPattern.cc
Expand Up @@ -9,6 +9,8 @@
#include "Image.h"
#include "CanvasPattern.h"

const cairo_user_data_key_t *pattern_repeat_key;

Nan::Persistent<FunctionTemplate> Pattern::constructor;

/*
Expand Down Expand Up @@ -57,24 +59,36 @@ NAN_METHOD(Pattern::New) {
} else if (Nan::New(Canvas::constructor)->HasInstance(obj)) {
Canvas *canvas = Nan::ObjectWrap::Unwrap<Canvas>(obj);
surface = canvas->surface();

// Invalid
} else {
return Nan::ThrowTypeError("Image or Canvas expected");
}

Pattern *pattern = new Pattern(surface);
repeat_type_t repeat = REPEAT;
if (0 == strcmp("no-repeat", *String::Utf8Value(info[1]))) {
repeat = NO_REPEAT;
} else if (0 == strcmp("repeat-x", *String::Utf8Value(info[1]))) {
repeat = REPEAT_X;
} else if (0 == strcmp("repeat-y", *String::Utf8Value(info[1]))) {
repeat = REPEAT_Y;
}
Pattern *pattern = new Pattern(surface, repeat);
pattern->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}


/*
* Initialize linear gradient.
*/

Pattern::Pattern(cairo_surface_t *surface) {
Pattern::Pattern(cairo_surface_t *surface, repeat_type_t repeat) {
_pattern = cairo_pattern_create_for_surface(surface);
_repeat = repeat;
cairo_pattern_set_user_data(_pattern, pattern_repeat_key, &_repeat, NULL);
}

repeat_type_t Pattern::get_repeat_type_for_cairo_pattern(cairo_pattern_t *pattern) {
void *ud = cairo_pattern_get_user_data(pattern, pattern_repeat_key);
return *reinterpret_cast<repeat_type_t*>(ud);
}

/*
Expand Down
19 changes: 16 additions & 3 deletions src/CanvasPattern.h
Expand Up @@ -10,18 +10,31 @@

#include "Canvas.h"

/*
* Canvas types.
*/

typedef enum {
NO_REPEAT, // match CAIRO_EXTEND_NONE
REPEAT, // match CAIRO_EXTEND_REPEAT
REPEAT_X, // needs custom processing
REPEAT_Y // needs custom processing
} repeat_type_t;

extern const cairo_user_data_key_t *pattern_repeat_key;

class Pattern: public Nan::ObjectWrap {
public:
static Nan::Persistent<FunctionTemplate> constructor;
static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
static NAN_METHOD(New);
Pattern(cairo_surface_t *surface);
static repeat_type_t get_repeat_type_for_cairo_pattern(cairo_pattern_t *pattern);
Pattern(cairo_surface_t *surface, repeat_type_t repeat);
inline cairo_pattern_t *pattern(){ return _pattern; }

private:
~Pattern();
// TODO REPEAT/REPEAT_X/REPEAT_Y
cairo_pattern_t *_pattern;
repeat_type_t _repeat;
};

#endif
16 changes: 13 additions & 3 deletions src/CanvasRenderingContext2d.cc
Expand Up @@ -312,8 +312,13 @@ void
Context2d::fill(bool preserve) {
if (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
repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->fillPattern);
if (NO_REPEAT == repeat) {
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE);
} else {
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
}
// TODO repeat-x/repeat-y
} else if (state->fillGradient) {
cairo_pattern_set_filter(state->fillGradient, state->patternQuality);
cairo_set_source(_context, state->fillGradient);
Expand All @@ -340,7 +345,12 @@ void
Context2d::stroke(bool preserve) {
if (state->strokePattern) {
cairo_set_source(_context, state->strokePattern);
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_REPEAT);
repeat_type_t repeat = Pattern::get_repeat_type_for_cairo_pattern(state->strokePattern);
if (NO_REPEAT == repeat) {
cairo_pattern_set_extend(cairo_get_source(_context), CAIRO_EXTEND_NONE);
} else {
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);
Expand Down
17 changes: 17 additions & 0 deletions test/public/tests.js
Expand Up @@ -371,6 +371,23 @@ tests['clip() 2'] = function(ctx){
}
};

tests['createPattern() no-repeat'] = function(ctx, done) {
var img = new Image;
img.onload = function(){
ctx.scale(0.1, 0.1);
ctx.lineStyle = 'black';
ctx.lineWidth = 10;
ctx.fillStyle = ctx.createPattern(img, 'no-repeat');;
ctx.fillRect(0, 0, 900, 900);
ctx.strokeRect(0, 0, 900, 900);
ctx.fillStyle = ctx.createPattern(img, 'repeat');;
ctx.fillRect(1000, 1000, 900, 900);
ctx.strokeRect(1000, 1000, 900, 900);
done();
};
img.src = 'face.jpeg';
};

tests['createLinearGradient()'] = function(ctx){
var lingrad = ctx.createLinearGradient(0,0,0,150);
lingrad.addColorStop(0, '#00ABEB');
Expand Down

0 comments on commit 08a70e2

Please sign in to comment.