From eddbc2517100c4f743cb24f59bc561cf3b563bfa Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Fri, 25 Feb 2011 12:46:45 +0100 Subject: [PATCH] Implement Item#blendMode. --- src/document/Doc.js | 2 +- src/item/Group.js | 41 ++++++++++-------- src/item/Item.js | 15 ++++++- src/item/PlacedSymbol.js | 17 +++++--- src/item/Raster.js | 17 +++++--- src/path/CompoundPath.js | 36 ++++++++------- src/path/Path.js | 94 +++++++++++++++++++++------------------- 7 files changed, 131 insertions(+), 91 deletions(-) diff --git a/src/document/Doc.js b/src/document/Doc.js index 8b5136da..83c37d33 100644 --- a/src/document/Doc.js +++ b/src/document/Doc.js @@ -33,7 +33,7 @@ Doc = Base.extend({ // this.canvas.width = this.canvas.width might be faster.. this.ctx.clearRect(0, 0, this.size.width + 1, this.size.height); for (var i = 0, l = this.layers.length; i < l; i++) { - this.layers[i].draw(this.ctx); + this.layers[i].draw(this.ctx, {}); } } } diff --git a/src/item/Group.js b/src/item/Group.js index 552cd5c6..c5e92c5e 100644 --- a/src/item/Group.js +++ b/src/item/Group.js @@ -11,30 +11,35 @@ Group = Item.extend({ this.clipped = false; }, - draw: function(ctx) { + draw: function(ctx, param) { if (!this.visible) return; // If the group has an opacity of less then 1, draw its children on a // temporary canvas, and then draw that canvas onto ctx afterwards // with globalAlpha set. var tempCanvas, originalCtx; - if (this.opacity < 1) { - var originalCtx = ctx; - tempCanvas = CanvasProvider.getCanvas(this.document.size); - ctx = tempCanvas.getContext('2d'); - } - for (var i = 0, l = this.children.length; i < l; i++) { - this.children[i].draw(ctx); - if (this.clipped & i == 0) - ctx.clip(); - } - if (tempCanvas) { - originalCtx.save(); - originalCtx.globalAlpha = this.opacity; - originalCtx.drawImage(tempCanvas, 0, 0); - originalCtx.restore(); - // Return the canvas, so it can be reused - CanvasProvider.returnCanvas(tempCanvas); + if(this.blendMode != 'normal' && !param.ignoreBlendMode) { + BlendMode.process(ctx, this, param); + } else { + param.ignoreBlendMode = false; + if (this.opacity < 1) { + var originalCtx = ctx; + tempCanvas = CanvasProvider.getCanvas(this.document.size); + ctx = tempCanvas.getContext('2d'); + } + for (var i = 0, l = this.children.length; i < l; i++) { + this.children[i].draw(ctx, param); + if (this.clipped & i == 0) + ctx.clip(); + } + if (tempCanvas) { + originalCtx.save(); + originalCtx.globalAlpha = this.opacity; + originalCtx.drawImage(tempCanvas, 0, 0); + originalCtx.restore(); + // Return the canvas, so it can be reused + CanvasProvider.returnCanvas(tempCanvas); + } } }, diff --git a/src/item/Item.js b/src/item/Item.js index dad79723..5af9a6c8 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -87,6 +87,20 @@ Item = Base.extend({ opacity: 1, + /** + * The blend mode of the item. + * + * Sample code: + * + * var circle = new Path.Circle(new Point(50, 50), 10); + * print(circle.blendMode); // normal + * + * // Change the blend mode of the path item: + * circle.blendMode = 'multiply'; + * + */ + blendMode: 'normal', + /** * Specifies whether the item is hidden. * @@ -137,7 +151,6 @@ Item = Base.extend({ } }, - // TODO: getBlendMode / setBlendMode // TODO: getIsolated / setIsolated (print specific feature) // TODO: get/setKnockout (print specific feature) // TODO get/setAlphaIsShape diff --git a/src/item/PlacedSymbol.js b/src/item/PlacedSymbol.js index 2c38bcb7..92fc022c 100644 --- a/src/item/PlacedSymbol.js +++ b/src/item/PlacedSymbol.js @@ -38,12 +38,17 @@ PlacedSymbol = Item.extend({ return this._bounds; }, - draw: function(ctx) { - // TODO: we need to preserve strokewidth, but still transform the fill - ctx.save(); - this.matrix.applyToContext(ctx); - this.symbol.definition.draw(ctx); - ctx.restore(); + draw: function(ctx, param) { + if(this.blendMode != 'normal' && !param.ignoreBlendMode) { + BlendMode.process(ctx, this, param); + } else { + param.ignoreBlendMode = false; + // TODO: we need to preserve strokewidth, but still transform the fill + ctx.save(); + this.matrix.applyToContext(ctx); + this.symbol.definition.draw(ctx); + ctx.restore(); + } } // TODO: // embed() diff --git a/src/item/Raster.js b/src/item/Raster.js index f66468ca..ce75016b 100644 --- a/src/item/Raster.js +++ b/src/item/Raster.js @@ -165,12 +165,17 @@ Raster = Item.extend({ return this._bounds; }, - draw: function(ctx) { - ctx.save(); - this.matrix.applyToContext(ctx); - ctx.drawImage(this._canvas || this._image, - -this.size.width / 2, -this.size.height / 2); - ctx.restore(); + draw: function(ctx, param) { + if(this.blendMode != 'normal' && !param.ignoreBlendMode) { + BlendMode.process(ctx, this, param); + } else { + param.ignoreBlendMode = false; + ctx.save(); + this.matrix.applyToContext(ctx); + ctx.drawImage(this._canvas || this._image, + -this.size.width / 2, -this.size.height / 2); + ctx.restore(); + } } }, new function() { function getAverageColor(pixels) { diff --git a/src/path/CompoundPath.js b/src/path/CompoundPath.js index c54a6057..e20a262e 100644 --- a/src/path/CompoundPath.js +++ b/src/path/CompoundPath.js @@ -19,24 +19,30 @@ CompoundPath = PathItem.extend(new function() { } }, - draw: function(ctx) { + draw: function(ctx, param) { if(!this.visible) return; if (this.children.length) { - var firstChild = this.children[0]; - ctx.beginPath(); - for (var i = 0, l = this.children.length; i < l; i++) { - var child = this.children[i]; - child.draw(ctx, true); - } - firstChild.setCtxStyles(ctx); - if (firstChild.fillColor) { - ctx.fillStyle = firstChild.fillColor.getCssString(); - ctx.fill(); - } - if (firstChild.strokeColor) { - ctx.strokeStyle = firstChild.strokeColor.getCssString(); - ctx.stroke(); + if(this.blendMode && !param.ignoreBlendMode) { + BlendMode.process(ctx, this, param); + } else { + var firstChild = this.children[0]; + ctx.beginPath(); + param.compound = true; + for (var i = 0, l = this.children.length; i < l; i++) { + var child = this.children[i]; + child.draw(ctx, param); + } + param.compound = false; + firstChild.setCtxStyles(ctx); + if (firstChild.fillColor) { + ctx.fillStyle = firstChild.fillColor.getCssString(); + ctx.fill(); + } + if (firstChild.strokeColor) { + ctx.strokeStyle = firstChild.strokeColor.getCssString(); + ctx.stroke(); + } } } }, diff --git a/src/path/Path.js b/src/path/Path.js index 9ebe308f..91110f62 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -352,57 +352,63 @@ Path = PathItem.extend({ this.closed = ture; }, - draw: function(ctx, compound) { + draw: function(ctx, param) { if (!this.visible) return; - if (!compound) - ctx.beginPath(); - - var segments = this._segments; - var length = segments.length; - for (var i = 0; i < length; i++) { - var segment = segments[i]; - var x = segment.point.x; - var y = segment.point.y; - var handleIn = segment.handleIn; - if (i == 0) { - ctx.moveTo(x, y); - } else { - if (handleOut.isZero() && handleIn.isZero()) { - ctx.lineTo(x, y); + if(this.blendMode != 'normal' && !param.ignoreBlendMode) { + BlendMode.process(ctx, this, param); + } else { + param.ignoreBlendMode = false; + if (!param.compound) + ctx.beginPath(); + + var segments = this._segments; + var length = segments.length; + for (var i = 0; i < length; i++) { + var segment = segments[i]; + var x = segment.point.x; + var y = segment.point.y; + var handleIn = segment.handleIn; + if (i == 0) { + ctx.moveTo(x, y); } else { - ctx.bezierCurveTo( - outX, outY, - handleIn.x + x, handleIn.y + y, - x, y - ); + if (handleOut.isZero() && handleIn.isZero()) { + ctx.lineTo(x, y); + } else { + ctx.bezierCurveTo( + outX, outY, + handleIn.x + x, handleIn.y + y, + x, y + ); + } } + var handleOut = segment.handleOut; + var outX = handleOut.x + x; + var outY = handleOut.y + y; } - var handleOut = segment.handleOut; - var outX = handleOut.x + x; - var outY = handleOut.y + y; - } - if (this.closed && length > 1) { - var segment = segments[0]; - var x = segment.point.x; - var y = segment.point.y; - var handleIn = segment.handleIn; - ctx.bezierCurveTo(outX, outY, handleIn.x + x, handleIn.y + y, x, y); - ctx.closePath(); - } - if (!compound) { - this.setCtxStyles(ctx); - ctx.save(); - ctx.globalAlpha = this.opacity; - if (this.fillColor) { - ctx.fillStyle = this.fillColor.getCanvasStyle(ctx); - ctx.fill(); + if (this.closed && length > 1) { + var segment = segments[0]; + var x = segment.point.x; + var y = segment.point.y; + var handleIn = segment.handleIn; + ctx.bezierCurveTo(outX, outY, handleIn.x + x, handleIn.y + y, x, y); + ctx.closePath(); } - if (this.strokeColor) { - ctx.strokeStyle = this.strokeColor.getCanvasStyle(ctx); - ctx.stroke(); + if (!param.compound) { + this.setCtxStyles(ctx); + ctx.save(); + ctx.globalAlpha = this.opacity; + if (this.fillColor) { + ctx.fillStyle = this.fillColor.getCanvasStyle(ctx); + ctx.fill(); + } + if (this.strokeColor) { + ctx.strokeStyle = this.strokeColor.getCanvasStyle(ctx); + ctx.stroke(); + } + ctx.restore(); } - ctx.restore(); } + } }, new function() { // inject methods that require scoped privates /**