Skip to content

Commit 75acdf1

Browse files
committed
Add support for direct drawing of native blendModes for items that allow it.
Bypassing compositing into separate canvases completely.
1 parent ad6124c commit 75acdf1

3 files changed

Lines changed: 41 additions & 28 deletions

File tree

src/core/PaperScope.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ var PaperScope = Base.extend(/** @lends PaperScope# */{
6464
// describe the support of various features.
6565
var ctx = CanvasProvider.getContext(1, 1);
6666
PaperScope.prototype.support = {
67-
dash: 'setLineDash' in ctx || 'mozDash' in ctx
67+
nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
68+
nativeBlend: BlendMode.nativeModes
6869
};
6970
CanvasProvider.release(ctx);
7071
}

src/item/Item.js

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,7 +2848,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
28482848
ctx.fillStyle = fillColor.toCanvasStyle(ctx);
28492849
if (strokeColor) {
28502850
ctx.strokeStyle = strokeColor.toCanvasStyle(ctx);
2851-
if (paper.support.dash && dashArray && dashArray.length) {
2851+
if (paper.support.nativeDash && dashArray && dashArray.length) {
28522852
if ('setLineDash' in ctx) {
28532853
ctx.setLineDash(dashArray);
28542854
ctx.lineDashOffset = dashOffset;
@@ -2858,11 +2858,6 @@ var Item = Base.extend(Callback, /** @lends Item# */{
28582858
}
28592859
}
28602860
}
2861-
// If the item only defines a strokeColor or a fillColor, draw it
2862-
// directly with the globalAlpha set, otherwise we will do it later when
2863-
// we composite the temporary canvas.
2864-
if (!fillColor || !strokeColor)
2865-
ctx.globalAlpha = this._opacity;
28662861
},
28672862

28682863
// TODO: Implement View into the drawing.
@@ -2891,51 +2886,68 @@ var Item = Base.extend(Callback, /** @lends Item# */{
28912886
// Exclude Raster items since they never draw a stroke and handle
28922887
// opacity by themselves (they also don't call _setStyles)
28932888
var blendMode = this._blendMode,
2894-
parentCtx, itemOffset, prevOffset;
2895-
if (blendMode !== 'normal' || this._opacity < 1
2896-
&& this._type !== 'raster' && (this._type !== 'path'
2897-
|| this.hasFill() && this.hasStroke())) {
2889+
opacity = this._opacity,
2890+
type = this._type,
2891+
nativeBlend = BlendMode.nativeModes[blendMode],
2892+
// Determine if we can draw directly, or if we need to draw into a
2893+
// separate canvas and then composite onto the main canvas.
2894+
direct = blendMode === 'normal' && opacity === 1
2895+
// If blending natively is possible, see if the type of item
2896+
// and its color settings allow it. A path with only a fill
2897+
// or a stroke can be directly blended, but if it has both,
2898+
// it needs to be drawn into a separate canvas first.
2899+
|| (nativeBlend || opacity < 1) && (type === 'raster'
2900+
|| (type === 'path' || type === 'compound-path')
2901+
&& !(this.hasFill() && this.hasStroke())),
2902+
mainCtx, itemOffset, prevOffset;
2903+
if (!direct) {
28982904
// Apply the paren't global matrix to the calculation of correct
28992905
// bounds.
29002906
var bounds = this.getStrokeBounds(parentMatrix);
29012907
if (!bounds.width || !bounds.height)
29022908
return;
2903-
// Store previous offset and save the parent context, so we can
2904-
// draw onto it later
2909+
// Store previous offset and save the main context, so we can
2910+
// draw onto it later.
29052911
prevOffset = param.offset;
29062912
// Floor the offset and ceil the size, so we don't cut off any
29072913
// antialiased pixels when drawing onto the temporary canvas.
29082914
itemOffset = param.offset = bounds.getTopLeft().floor();
2909-
// Set ctx to the context of the temporary canvas,
2910-
// so we draw onto it, instead of the parentCtx
2911-
parentCtx = ctx;
2915+
// Set ctx to the context of the temporary canvas, so we draw onto
2916+
// it, instead of the mainCtx.
2917+
mainCtx = ctx;
29122918
ctx = CanvasProvider.getContext(
29132919
bounds.getSize().ceil().add(new Size(1, 1)));
29142920
}
29152921
ctx.save();
2916-
// Translate the context so the topLeft of the item is at (0, 0)
2917-
// on the temporary canvas.
2918-
if (parentCtx)
2922+
// If drawing directly, handle opacity and native blending now,
2923+
// otherwise we will do it later when the temporary canvas is composited.
2924+
if (direct) {
2925+
ctx.globalAlpha = opacity;
2926+
if (nativeBlend)
2927+
ctx.globalCompositeOperation = blendMode;
2928+
} else {
2929+
// Translate the context so the topLeft of the item is at (0, 0)
2930+
// on the temporary canvas.
29192931
ctx.translate(-itemOffset.x, -itemOffset.y);
2920-
// Apply globalMatrix when blitting into temporary canvas.
2921-
(parentCtx ? globalMatrix : this._matrix).applyToContext(ctx);
2932+
}
2933+
// Apply globalMatrix when drawing into temporary canvas.
2934+
(direct ? this._matrix : globalMatrix).applyToContext(ctx);
29222935
// If we're drawing into a separate canvas and a clipItem is defined for
29232936
// the current rendering loop, we need to draw the clip item again.
2924-
if (parentCtx && param.clipItem)
2937+
if (!direct && param.clipItem)
29252938
param.clipItem.draw(ctx, param.extend({ clip: true }));
29262939
this._draw(ctx, param);
29272940
ctx.restore();
29282941
transforms.pop();
29292942
if (param.clip)
29302943
ctx.clip();
2931-
// If a temporary canvas was created before, composite it onto the
2932-
// parent canvas:
2933-
if (parentCtx) {
2944+
// If a temporary canvas was created, composite it onto the main canvas:
2945+
if (!direct) {
29342946
// Use BlendMode.process even for processing normal blendMode with
29352947
// opacity.
2936-
BlendMode.process(blendMode, ctx, parentCtx, this._opacity,
2948+
BlendMode.process(blendMode, ctx, mainCtx, opacity,
29372949
// Calculate the pixel offset of the temporary canvas to the
2938-
// parent canvas.
2950+
// main canvas.
29392951
itemOffset.subtract(prevOffset));
29402952
// Return the temporary context, so it can be reused
29412953
CanvasProvider.release(ctx);

src/path/Path.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1912,7 +1912,7 @@ var Path = PathItem.extend(/** @lends Path# */{
19121912
fillColor = style.getFillColor(),
19131913
strokeColor = style.getStrokeColor(),
19141914
dashArray = style.getDashArray(),
1915-
drawDash = !paper.support.dash && strokeColor
1915+
drawDash = !paper.support.nativeDash && strokeColor
19161916
&& dashArray && dashArray.length;
19171917

19181918
// Prepare the canvas path if we have any situation that requires it

0 commit comments

Comments
 (0)