From faae57a90c53940e18f76e3e30e9d207ba02dd03 Mon Sep 17 00:00:00 2001 From: wsw Date: Fri, 11 Mar 2016 12:43:18 +0800 Subject: [PATCH] Add support for evenodd fill rule --- examples/fill-evenodd.js | 13 +++ src/CanvasRenderingContext2d.cc | 15 +++ src/CanvasRenderingContext2d.h | 1 + test/canvas.test.js | 173 +++++++++++++++++++++++++++++++- 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 examples/fill-evenodd.js diff --git a/examples/fill-evenodd.js b/examples/fill-evenodd.js new file mode 100644 index 0000000..f71f3ae --- /dev/null +++ b/examples/fill-evenodd.js @@ -0,0 +1,13 @@ +var fs = require('fs') +var path = require('path') +var Canvas = require('..') + +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') + +canvas.createJPEGStream().pipe(fs.createWriteStream(path.join(__dirname, '/fill-evenodd.jpg'))) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index a21a56d..66064ea 100755 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -296,6 +296,18 @@ Context2d::restorePath() { * Fill and apply shadow. */ +void +Context2d::setFillRule(v8::Local 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) { @@ -1400,6 +1412,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(cairo_in_fill(ctx, x, y) || cairo_in_stroke(ctx, x, y))); return; } @@ -1677,6 +1690,7 @@ NAN_METHOD(Context2d::Scale) { NAN_METHOD(Context2d::Clip) { Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); + context->setFillRule(info[0]); cairo_t *ctx = context->context(); cairo_clip_preserve(ctx); } @@ -1687,6 +1701,7 @@ NAN_METHOD(Context2d::Clip) { NAN_METHOD(Context2d::Fill) { Context2d *context = Nan::ObjectWrap::Unwrap(info.This()); + context->setFillRule(info[0]); context->fill(true); } diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index 22dac4a..7d0b139 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -161,6 +161,7 @@ class Context2d: public Nan::ObjectWrap { void restorePath(); void saveState(); void restoreState(); + void inline setFillRule(v8::Local value); void fill(bool preserve = false); void stroke(bool preserve = false); void save(); diff --git a/test/canvas.test.js b/test/canvas.test.js index dedf874..d2da79b 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -833,5 +833,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')); + }); + });