Implement Item#blendMode.

This commit is contained in:
Jonathan Puckey 2011-02-25 12:46:45 +01:00
parent d9b75a7232
commit eddbc25171
7 changed files with 131 additions and 91 deletions

View file

@ -33,7 +33,7 @@ Doc = Base.extend({
// this.canvas.width = this.canvas.width might be faster.. // this.canvas.width = this.canvas.width might be faster..
this.ctx.clearRect(0, 0, this.size.width + 1, this.size.height); this.ctx.clearRect(0, 0, this.size.width + 1, this.size.height);
for (var i = 0, l = this.layers.length; i < l; i++) { for (var i = 0, l = this.layers.length; i < l; i++) {
this.layers[i].draw(this.ctx); this.layers[i].draw(this.ctx, {});
} }
} }
} }

View file

@ -11,30 +11,35 @@ Group = Item.extend({
this.clipped = false; this.clipped = false;
}, },
draw: function(ctx) { draw: function(ctx, param) {
if (!this.visible) if (!this.visible)
return; return;
// If the group has an opacity of less then 1, draw its children on a // 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 // temporary canvas, and then draw that canvas onto ctx afterwards
// with globalAlpha set. // with globalAlpha set.
var tempCanvas, originalCtx; var tempCanvas, originalCtx;
if (this.opacity < 1) { if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
var originalCtx = ctx; BlendMode.process(ctx, this, param);
tempCanvas = CanvasProvider.getCanvas(this.document.size); } else {
ctx = tempCanvas.getContext('2d'); param.ignoreBlendMode = false;
} if (this.opacity < 1) {
for (var i = 0, l = this.children.length; i < l; i++) { var originalCtx = ctx;
this.children[i].draw(ctx); tempCanvas = CanvasProvider.getCanvas(this.document.size);
if (this.clipped & i == 0) ctx = tempCanvas.getContext('2d');
ctx.clip(); }
} for (var i = 0, l = this.children.length; i < l; i++) {
if (tempCanvas) { this.children[i].draw(ctx, param);
originalCtx.save(); if (this.clipped & i == 0)
originalCtx.globalAlpha = this.opacity; ctx.clip();
originalCtx.drawImage(tempCanvas, 0, 0); }
originalCtx.restore(); if (tempCanvas) {
// Return the canvas, so it can be reused originalCtx.save();
CanvasProvider.returnCanvas(tempCanvas); originalCtx.globalAlpha = this.opacity;
originalCtx.drawImage(tempCanvas, 0, 0);
originalCtx.restore();
// Return the canvas, so it can be reused
CanvasProvider.returnCanvas(tempCanvas);
}
} }
}, },

View file

@ -87,6 +87,20 @@ Item = Base.extend({
opacity: 1, opacity: 1,
/**
* The blend mode of the item.
*
* Sample code:
* <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';
* </code>
*/
blendMode: 'normal',
/** /**
* Specifies whether the item is hidden. * Specifies whether the item is hidden.
* *
@ -137,7 +151,6 @@ Item = Base.extend({
} }
}, },
// TODO: getBlendMode / setBlendMode
// TODO: getIsolated / setIsolated (print specific feature) // TODO: getIsolated / setIsolated (print specific feature)
// TODO: get/setKnockout (print specific feature) // TODO: get/setKnockout (print specific feature)
// TODO get/setAlphaIsShape // TODO get/setAlphaIsShape

View file

@ -38,12 +38,17 @@ PlacedSymbol = Item.extend({
return this._bounds; return this._bounds;
}, },
draw: function(ctx) { draw: function(ctx, param) {
// TODO: we need to preserve strokewidth, but still transform the fill if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
ctx.save(); BlendMode.process(ctx, this, param);
this.matrix.applyToContext(ctx); } else {
this.symbol.definition.draw(ctx); param.ignoreBlendMode = false;
ctx.restore(); // 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: // TODO:
// embed() // embed()

View file

@ -165,12 +165,17 @@ Raster = Item.extend({
return this._bounds; return this._bounds;
}, },
draw: function(ctx) { draw: function(ctx, param) {
ctx.save(); if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
this.matrix.applyToContext(ctx); BlendMode.process(ctx, this, param);
ctx.drawImage(this._canvas || this._image, } else {
-this.size.width / 2, -this.size.height / 2); param.ignoreBlendMode = false;
ctx.restore(); ctx.save();
this.matrix.applyToContext(ctx);
ctx.drawImage(this._canvas || this._image,
-this.size.width / 2, -this.size.height / 2);
ctx.restore();
}
} }
}, new function() { }, new function() {
function getAverageColor(pixels) { function getAverageColor(pixels) {

View file

@ -19,24 +19,30 @@ CompoundPath = PathItem.extend(new function() {
} }
}, },
draw: function(ctx) { draw: function(ctx, param) {
if(!this.visible) if(!this.visible)
return; return;
if (this.children.length) { if (this.children.length) {
var firstChild = this.children[0]; if(this.blendMode && !param.ignoreBlendMode) {
ctx.beginPath(); BlendMode.process(ctx, this, param);
for (var i = 0, l = this.children.length; i < l; i++) { } else {
var child = this.children[i]; var firstChild = this.children[0];
child.draw(ctx, true); ctx.beginPath();
} param.compound = true;
firstChild.setCtxStyles(ctx); for (var i = 0, l = this.children.length; i < l; i++) {
if (firstChild.fillColor) { var child = this.children[i];
ctx.fillStyle = firstChild.fillColor.getCssString(); child.draw(ctx, param);
ctx.fill(); }
} param.compound = false;
if (firstChild.strokeColor) { firstChild.setCtxStyles(ctx);
ctx.strokeStyle = firstChild.strokeColor.getCssString(); if (firstChild.fillColor) {
ctx.stroke(); ctx.fillStyle = firstChild.fillColor.getCssString();
ctx.fill();
}
if (firstChild.strokeColor) {
ctx.strokeStyle = firstChild.strokeColor.getCssString();
ctx.stroke();
}
} }
} }
}, },

View file

@ -352,57 +352,63 @@ Path = PathItem.extend({
this.closed = ture; this.closed = ture;
}, },
draw: function(ctx, compound) { draw: function(ctx, param) {
if (!this.visible) return; if (!this.visible) return;
if (!compound) if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
ctx.beginPath(); BlendMode.process(ctx, this, param);
} else {
var segments = this._segments; param.ignoreBlendMode = false;
var length = segments.length; if (!param.compound)
for (var i = 0; i < length; i++) { ctx.beginPath();
var segment = segments[i];
var x = segment.point.x; var segments = this._segments;
var y = segment.point.y; var length = segments.length;
var handleIn = segment.handleIn; for (var i = 0; i < length; i++) {
if (i == 0) { var segment = segments[i];
ctx.moveTo(x, y); var x = segment.point.x;
} else { var y = segment.point.y;
if (handleOut.isZero() && handleIn.isZero()) { var handleIn = segment.handleIn;
ctx.lineTo(x, y); if (i == 0) {
ctx.moveTo(x, y);
} else { } else {
ctx.bezierCurveTo( if (handleOut.isZero() && handleIn.isZero()) {
outX, outY, ctx.lineTo(x, y);
handleIn.x + x, handleIn.y + y, } else {
x, y 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; if (this.closed && length > 1) {
var outX = handleOut.x + x; var segment = segments[0];
var outY = handleOut.y + y; var x = segment.point.x;
} var y = segment.point.y;
if (this.closed && length > 1) { var handleIn = segment.handleIn;
var segment = segments[0]; ctx.bezierCurveTo(outX, outY, handleIn.x + x, handleIn.y + y, x, y);
var x = segment.point.x; ctx.closePath();
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.strokeColor) { if (!param.compound) {
ctx.strokeStyle = this.strokeColor.getCanvasStyle(ctx); this.setCtxStyles(ctx);
ctx.stroke(); 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 }, new function() { // inject methods that require scoped privates
/** /**