Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support evenodd fill rule #762

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions examples/fill-evenodd.js
@@ -0,0 +1,25 @@

/**
* Module dependencies.
*/

var Canvas = require('../lib/canvas');
var fs = require('fs');

var canvas = new Canvas(100, 100);
var ctx = canvas.getContext('2d');

ctx.fillStyle = '#f00';
ctx.rect(0, 0, 100, 50);
ctx.arc(50, 50, 50, 0, 2 * Math.PI);
ctx.fill('evenodd');
//ctx.fill();

var out = fs.createWriteStream(__dirname + '/fill-evenodd.jpg');

var stream = canvas.createJPEGStream({
bufsize : 2048,
quality : 80
});

stream.pipe(out);
15 changes: 15 additions & 0 deletions src/CanvasRenderingContext2d.cc
Expand Up @@ -296,6 +296,18 @@ Context2d::restorePath() {
* Fill and apply shadow.
*/

void
Context2d::setFillRule(v8::Local<v8::Value> value) {
cairo_fill_rule_t rule = CAIRO_FILL_RULE_WINDING;
if (value->IsString()) {
String::Utf8Value str(value);
if (std::strcmp(*str, "evenodd") == 0) {
rule = CAIRO_FILL_RULE_EVEN_ODD;
}
}
cairo_set_fill_rule(_context, rule);
}

void
Context2d::fill(bool preserve) {
if (state->fillPattern) {
Expand Down Expand Up @@ -1398,6 +1410,7 @@ NAN_METHOD(Context2d::IsPointInPath) {
cairo_t *ctx = context->context();
double x = info[0]->NumberValue()
, y = info[1]->NumberValue();
context->setFillRule(info[2]);
info.GetReturnValue().Set(Nan::New<Boolean>(cairo_in_fill(ctx, x, y) || cairo_in_stroke(ctx, x, y)));
return;
}
Expand Down Expand Up @@ -1675,6 +1688,7 @@ NAN_METHOD(Context2d::Scale) {

NAN_METHOD(Context2d::Clip) {
Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
context->setFillRule(info[0]);
cairo_t *ctx = context->context();
cairo_clip_preserve(ctx);
}
Expand All @@ -1685,6 +1699,7 @@ NAN_METHOD(Context2d::Clip) {

NAN_METHOD(Context2d::Fill) {
Context2d *context = Nan::ObjectWrap::Unwrap<Context2d>(info.This());
context->setFillRule(info[0]);
context->fill(true);
}

Expand Down
1 change: 1 addition & 0 deletions src/CanvasRenderingContext2d.h
Expand Up @@ -161,6 +161,7 @@ class Context2d: public Nan::ObjectWrap {
void restorePath();
void saveState();
void restoreState();
void inline setFillRule(v8::Local<v8::Value> value);
void fill(bool preserve = false);
void stroke(bool preserve = false);
void save();
Expand Down
173 changes: 172 additions & 1 deletion test/canvas.test.js
Expand Up @@ -795,5 +795,176 @@ describe('Canvas', function () {
assert(chunk.length < SIZE);
});
s.on('end', done);
})
});

it('Context2d#fill()', function() {
var canvas = new Canvas(2, 2);
var ctx = canvas.getContext('2d');

// fill whole canvas with white
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, 2, 2);

var imageData, n;

// black
ctx.fillStyle = '#000';
ctx.rect(0, 0, 2, 1);
ctx.rect(1, 0, 1, 2);

ctx.fill('evenodd');
// b | w
// -----
// w | b
imageData = ctx.getImageData(0, 0, 2, 2);
// (0, 0) black
n = 0;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
// (0, 1) white
n = 1;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 0) white
n = 2;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 1) black
n = 3;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);

// should not retain previous value 'evenodd'
ctx.fill();
// b | b
// -----
// w | b
imageData = ctx.getImageData(0, 0, 2, 2);
// (0, 0) black
n = 0;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
// (0, 1) black
n = 1;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
// (1, 0) white
n = 2;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 1) black
n = 3;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
});

it('Context2d#clip()', function () {
var canvas = new Canvas(2, 2);
var ctx = canvas.getContext('2d');

// fill whole canvas with white
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, 2, 2);

var imageData, n;

// black
ctx.fillStyle = '#000';
ctx.rect(0, 0, 2, 1);
ctx.rect(1, 0, 1, 2);

ctx.clip('evenodd');
ctx.fillRect(0, 0, 2, 2);
// b | w
// -----
// w | b
imageData = ctx.getImageData(0, 0, 2, 2);
// (0, 0) black
n = 0;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
// (0, 1) white
n = 1;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 0) white
n = 2;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 1) black
n = 3;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);

ctx.clip();
ctx.fillRect(0, 0, 2, 2);
// b | b
// -----
// w | b
imageData = ctx.getImageData(0, 0, 2, 2);
// (0, 0) black
n = 0;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
// (0, 1) white
n = 1;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 0) white
n = 2;
assert.equal(imageData.data[n*4+0], 255);
assert.equal(imageData.data[n*4+1], 255);
assert.equal(imageData.data[n*4+2], 255);
assert.equal(imageData.data[n*4+3], 255);
// (1, 1) black
n = 3;
assert.equal(imageData.data[n*4+0], 0);
assert.equal(imageData.data[n*4+1], 0);
assert.equal(imageData.data[n*4+2], 0);
assert.equal(imageData.data[n*4+3], 255);
});

it('Context2d#IsPointInPath()', function () {
var canvas = new Canvas(4, 4);
var ctx = canvas.getContext('2d');

ctx.rect(0, 0, 4, 2);
ctx.rect(2, 0, 2, 4);
ctx.stroke();

assert.ok(ctx.isPointInPath(1, 1, 'evenodd'));
assert.ok(!ctx.isPointInPath(3, 1, 'evenodd'));
assert.ok(ctx.isPointInPath(3, 1));
assert.ok(!ctx.isPointInPath(1, 3, 'evenodd'));
assert.ok(ctx.isPointInPath(3, 3, 'evenodd'));
});

});