diff --git a/src/item/Group.js b/src/item/Group.js index c74a8b7e..e667a232 100644 --- a/src/item/Group.js +++ b/src/item/Group.js @@ -168,13 +168,13 @@ var Group = this.Group = Item.extend(/** @lends Group# */{ var clipItem = this._getClipItem(); if (clipItem) { param.clip = true; - Item.draw(clipItem, ctx, param); + clipItem.draw(ctx, param); param.clip = false; } for (var i = 0, l = this._children.length; i < l; i++) { var item = this._children[i]; - if (item != clipItem) - Item.draw(item, ctx, param); + if (item !== clipItem) + item.draw(ctx, param); } } }); diff --git a/src/item/Item.js b/src/item/Item.js index 64a59f0c..5cb7861a 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -1263,7 +1263,7 @@ var Item = this.Item = Base.extend(Callback, { matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); ctx.save(); matrix.applyToContext(ctx); - Item.draw(this, ctx, { transforms: [matrix] }); + this.draw(ctx, { transforms: [matrix] }); var raster = new Raster(canvas); raster.setBounds(bounds); ctx.restore(); @@ -2770,6 +2770,89 @@ var Item = this.Item = Base.extend(Callback, { ctx.globalAlpha = this._opacity; }, + // TODO: Implement View into the drawing. + // TODO: Optimize temporary canvas drawing to ignore parts that are + // outside of the visible view. + draw: function(ctx, param) { + if (!this._visible || this._opacity == 0) + return; + // Each time the project gets drawn, it's _drawCount is increased. + // Keep the _drawCount of drawn items in sync, so we have an easy + // way to filter out selected items that are not being drawn, e.g. + // because they are currently not part of the DOM. + this._drawCount = this._project._drawCount; + // Keep calculating the current global matrix, by keeping a history + // and pushing / popping as we go along. + var transforms = param.transforms, + parentMatrix = transforms[transforms.length - 1], + globalMatrix = parentMatrix.clone().concatenate(this._matrix); + transforms.push(this._globalMatrix = globalMatrix); + // If the item has a blendMode or is defining an opacity, draw it on + // a temporary canvas first and composite the canvas afterwards. + // Paths with an opacity < 1 that both define a fillColor + // and strokeColor also need to be drawn on a temporary canvas + // first, since otherwise their stroke is drawn half transparent + // over their fill. + // Exclude Raster items since they never draw a stroke and handle + // opacity by themselves (they also don't call _setStyles) + var parentCtx, itemOffset, prevOffset; + if (this._blendMode !== 'normal' || this._opacity < 1 + && this._type !== 'raster' && (this._type !== 'path' + || this.getFillColor() && this.getStrokeColor())) { + // Apply the paren't global matrix to the calculation of correct + // bounds. + var bounds = this.getStrokeBounds(parentMatrix); + if (!bounds.width || !bounds.height) + return; + // Store previous offset and save the parent context, so we can + // draw onto it later + prevOffset = param.offset; + // Floor the offset and ceil the size, so we don't cut off any + // antialiased pixels when drawing onto the temporary canvas. + itemOffset = param.offset = bounds.getTopLeft().floor(); + // Set ctx to the context of the temporary canvas, + // so we draw onto it, instead of the parentCtx + parentCtx = ctx; + ctx = CanvasProvider.getContext( + bounds.getSize().ceil().add(Size.create(1, 1))); + } + ctx.save(); + // Translate the context so the topLeft of the item is at (0, 0) + // on the temporary canvas. + if (parentCtx) + ctx.translate(-itemOffset.x, -itemOffset.y); + // Apply globalMatrix when blitting into temporary canvas. + (parentCtx ? globalMatrix : this._matrix).applyToContext(ctx); + this._draw(ctx, param); + ctx.restore(); + transforms.pop(); + if (param.clip) + ctx.clip(); + // If a temporary canvas was created before, composite it onto the + // parent canvas: + if (parentCtx) { + // Restore previous offset. + param.offset = prevOffset; + // If the item has a blendMode, use BlendMode#process to + // composite its canvas on the parentCanvas. + if (this._blendMode !== 'normal') { + // The pixel offset of the temporary canvas to the parent + // canvas. + BlendMode.process(this._blendMode, ctx, parentCtx, + this._opacity, itemOffset.subtract(prevOffset)); + } else { + // Otherwise just set the globalAlpha before drawing the + // temporary canvas on the parent canvas. + parentCtx.save(); + parentCtx.globalAlpha = this._opacity; + parentCtx.drawImage(ctx.canvas, itemOffset.x, itemOffset.y); + parentCtx.restore(); + } + // Return the temporary context, so it can be reused + CanvasProvider.release(ctx); + } + }, + statics: { drawSelectedBounds: function(bounds, ctx, matrix) { var coords = matrix._transformCorners(bounds); @@ -2783,89 +2866,6 @@ var Item = this.Item = Base.extend(Callback, { ctx.rect(coords[i] - 2, coords[++i] - 2, 4, 4); ctx.fill(); } - }, - - // TODO: Implement View into the drawing. - // TODO: Optimize temporary canvas drawing to ignore parts that are - // outside of the visible view. - draw: function(item, ctx, param) { - if (!item._visible || item._opacity == 0) - return; - // Each time the project gets drawn, it's _drawCount is increased. - // Keep the _drawCount of drawn items in sync, so we have an easy - // way to filter out selected items that are not being drawn, e.g. - // because they are currently not part of the DOM. - item._drawCount = item._project._drawCount; - // Keep calculating the current global matrix, by keeping a history - // and pushing / popping as we go along. - var transforms = param.transforms, - parentMatrix = transforms[transforms.length - 1], - globalMatrix = parentMatrix.clone().concatenate(item._matrix); - transforms.push(item._globalMatrix = globalMatrix); - // If the item has a blendMode or is defining an opacity, draw it on - // a temporary canvas first and composite the canvas afterwards. - // Paths with an opacity < 1 that both define a fillColor - // and strokeColor also need to be drawn on a temporary canvas - // first, since otherwise their stroke is drawn half transparent - // over their fill. - // Exclude Raster items since they never draw a stroke and handle - // opacity by themselves (they also don't call _setStyles) - var parentCtx, itemOffset, prevOffset; - if (item._blendMode !== 'normal' || item._opacity < 1 - && item._type !== 'raster' && (item._type !== 'path' - || item.getFillColor() && item.getStrokeColor())) { - // Apply the paren't global matrix to the calculation of correct - // bounds. - var bounds = item.getStrokeBounds(parentMatrix); - if (!bounds.width || !bounds.height) - return; - // Store previous offset and save the parent context, so we can - // draw onto it later - prevOffset = param.offset; - // Floor the offset and ceil the size, so we don't cut off any - // antialiased pixels when drawing onto the temporary canvas. - itemOffset = param.offset = bounds.getTopLeft().floor(); - // Set ctx to the context of the temporary canvas, - // so we draw onto it, instead of the parentCtx - parentCtx = ctx; - ctx = CanvasProvider.getContext( - bounds.getSize().ceil().add(Size.create(1, 1))); - } - ctx.save(); - // Translate the context so the topLeft of the item is at (0, 0) - // on the temporary canvas. - if (parentCtx) - ctx.translate(-itemOffset.x, -itemOffset.y); - // Apply globalMatrix when blitting into temporary canvas. - (parentCtx ? globalMatrix : item._matrix).applyToContext(ctx); - item._draw(ctx, param); - ctx.restore(); - transforms.pop(); - if (param.clip) - ctx.clip(); - // If a temporary canvas was created before, composite it onto the - // parent canvas: - if (parentCtx) { - // Restore previous offset. - param.offset = prevOffset; - // If the item has a blendMode, use BlendMode#process to - // composite its canvas on the parentCanvas. - if (item._blendMode !== 'normal') { - // The pixel offset of the temporary canvas to the parent - // canvas. - BlendMode.process(item._blendMode, ctx, parentCtx, - item._opacity, itemOffset.subtract(prevOffset)); - } else { - // Otherwise just set the globalAlpha before drawing the - // temporary canvas on the parent canvas. - parentCtx.save(); - parentCtx.globalAlpha = item._opacity; - parentCtx.drawImage(ctx.canvas, itemOffset.x, itemOffset.y); - parentCtx.restore(); - } - // Return the temporary context, so it can be reused - CanvasProvider.release(ctx); - } } } }, Base.each(['down', 'drag', 'up', 'move'], function(name) { diff --git a/src/item/PlacedSymbol.js b/src/item/PlacedSymbol.js index 004f5e13..a6c73f7f 100644 --- a/src/item/PlacedSymbol.js +++ b/src/item/PlacedSymbol.js @@ -111,7 +111,7 @@ var PlacedSymbol = this.PlacedSymbol = PlacedItem.extend(/** @lends PlacedSymbol }, _draw: function(ctx, param) { - Item.draw(this.symbol._definition, ctx, param); + this.symbol._definition.draw(ctx, param); } // TODO: PlacedSymbol#embed() diff --git a/src/item/Raster.js b/src/item/Raster.js index 72b64579..25320650 100644 --- a/src/item/Raster.js +++ b/src/item/Raster.js @@ -400,7 +400,7 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{ matrix.applyToContext(ctx); // If a path was passed, draw it as a clipping mask: if (path) - Item.draw(path, ctx, { clip: true, transforms: [matrix] }); + path.draw(ctx, { clip: true, transforms: [matrix] }); // Now draw the image clipped into it. this._matrix.applyToContext(ctx); ctx.drawImage(this.getElement(), @@ -541,7 +541,7 @@ var Raster = this.Raster = PlacedItem.extend(/** @lends Raster# */{ var element = this.getElement(); if (element) { // Handle opacity for Rasters separately from the rest, since - // Rasters never draw a stroke. See Item.draw(). + // Rasters never draw a stroke. See Item#draw(). ctx.globalAlpha = this._opacity; ctx.drawImage(element, -this._size.width / 2, -this._size.height / 2); diff --git a/src/path/CompoundPath.js b/src/path/CompoundPath.js index 20f81743..6d9c47d2 100644 --- a/src/path/CompoundPath.js +++ b/src/path/CompoundPath.js @@ -204,7 +204,7 @@ var CompoundPath = this.CompoundPath = PathItem.extend(/** @lends CompoundPath# ctx.beginPath(); param.compound = true; for (var i = 0, l = children.length; i < l; i++) - Item.draw(children[i], ctx, param); + children[i].draw(ctx, param); param.compound = false; if (!param.clip) { this._setStyles(ctx); diff --git a/src/project/Project.js b/src/project/Project.js index ebe8b4e2..607c00b4 100644 --- a/src/project/Project.js +++ b/src/project/Project.js @@ -56,7 +56,7 @@ var Project = this.Project = PaperScopeItem.extend(/** @lends Project# */{ this._currentStyle = new Style(); this._selectedItems = {}; this._selectedItemCount = 0; - // See Item.draw() for an explanation of _drawCount + // See Item#draw() for an explanation of _drawCount this._drawCount = 0; // Change tracking, not in use for now. Activate once required: // this._changes = []; @@ -301,7 +301,7 @@ var Project = this.Project = PaperScopeItem.extend(/** @lends Project# */{ transforms: [matrix] }; for (var i = 0, l = this.layers.length; i < l; i++) - Item.draw(this.layers[i], ctx, param); + this.layers[i].draw(ctx, param); ctx.restore(); // Draw the selection of the selected items in the project: