diff --git a/src/anm/animation/element.js b/src/anm/animation/element.js index 39b0f564..4bee9092 100644 --- a/src/anm/animation/element.js +++ b/src/anm/animation/element.js @@ -744,101 +744,141 @@ Element.prototype._checkSwitcher = function(next) { */ // > Element.render % (ctx: Context, gtime: Float, dt: Float) Element.prototype.render = function(ctx) { - if (this.disabled || !this.active || this.isMask) return; + if (this.disabled || !this.active) return; this.rendering = true; if (this.visible) { - ctx.save(); - if (!this.$mask) { - // draw directly to context, if has no mask + if (!this.isMask && !this.$mask) { + + ctx.save(); + this.transform(ctx); this.painters(ctx); this.each(function(child) { child.render(ctx); }); - } else { - // FIXME: the complete mask process should be a Painter. - var mask = this.$mask; - - mask.ensureHasMaskCanvas(); - var mcvs = mask.__maskCvs, - mctx = mask.__maskCtx, - bcvs = mask.__backCvs, - bctx = mask.__backCtx; - - // FIXME: test if bounds are not empty - var bounds_pts = mask.bounds().toPoints(); - - var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, - maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; - - var pt; - for (var i = 0, il = bounds_pts.length; i < il; i++) { - pt = bounds_pts[i]; - if (pt.x < minX) minX = pt.x; - if (pt.y < minY) minY = pt.y; - if (pt.x > maxX) maxX = pt.x; - if (pt.y > maxY) maxY = pt.y; - } - var ratio = engine.PX_RATIO, - x = minX, y = minY, - width = Math.round(maxX - minX), - height = Math.round(maxY - minY); + ctx.restore(); - var last_cvs_size = this._maskCvsSize || engine.getCanvasSize(mcvs); + } else if (this.isMask) { // this element is a mask - if ((last_cvs_size[0] < width) || - (last_cvs_size[1] < height)) { - // mcvs/bcvs both always have the same size, so we save/check only one of them - this._maskCvsSize = engine.setCanvasSize(mcvs, width, height); - engine.setCanvasSize(bcvs, width, height); - } else { - this._maskCvsSize = last_cvs_size; - } + this._renderAsMask(); - var scale = ratio; // multiple by global scale when it's known + } else if (this.$mask) { // this element is masked - bctx.clearRect(0, 0, width*scale, height*scale); - mctx.clearRect(0, 0, width*scale, height*scale); + ctx.save(); + this._renderAsMasked(ctx); + ctx.restore(); - bctx.save(); - mctx.save(); + } - bctx.setTransform(scale, 0, 0, scale, -x*scale, -y*scale); - mctx.setTransform(scale, 0, 0, scale, -x*scale, -y*scale); + } - this.transform(bctx); - this.painters(bctx); - this.each(function(child) { - child.render(bctx, dt); - }); + this.__postRender(); + this.rendering = false; + return this; +}; - // FIXME: this should be performed one time for all masked elements! - mask.transform(mctx); - mask.painters(mctx); - mask.each(function(child) { - child.render(mctx); - }); +Element.prototype._renderAsMask = function() { + this.ensureHasMaskCanvas(); - bctx.globalCompositeOperation = 'destination-in'; - bctx.setTransform(1, 0, 0, 1, 0, 0); - bctx.drawImage(mcvs, 0, 0); + var mcvs = this.__maskCvs, + mctx = this.__maskCtx, + bcvs = this.__backCvs; - ctx.drawImage(bcvs, - 0, 0, Math.floor(width * scale), Math.floor(height * scale), - x, y, width, height); + // FIXME: test if bounds are not empty + var bounds_pts = this.bounds().toPoints(); - mctx.restore(); - bctx.restore(); - } - ctx.restore(); + var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, + maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; + + var pt; + for (var i = 0, il = bounds_pts.length; i < il; i++) { + pt = bounds_pts[i]; + if (pt.x < minX) minX = pt.x; + if (pt.y < minY) minY = pt.y; + if (pt.x > maxX) maxX = pt.x; + if (pt.y > maxY) maxY = pt.y; } - this.__postRender(); - this.rendering = false; - return this; + + var ratio = engine.PX_RATIO, + x = minX, y = minY, + width = Math.round(maxX - minX), + height = Math.round(maxY - minY); + + var last_cvs_size = this._maskCvsSize || engine.getCanvasSize(mcvs); + + var scale = ratio; // multiple by global scale when it's known + + if ((last_cvs_size[0] < width) || + (last_cvs_size[1] < height)) { + // mcvs/bcvs both always have the same size, so we save/check only one of them + this._maskCvsSize = engine.setCanvasSize(mcvs, width, height); + engine.setCanvasSize(bcvs, width, height); + } else { + this._maskCvsSize = last_cvs_size; + } + + this._maskCvsPos = [ x, y ]; + + mctx.clearRect(0, 0, width*scale, height*scale); + + mctx.save(); + mctx.setTransform(scale, 0, 0, scale, -x*scale, -y*scale); + + // FIXME: this should be performed one time for all masked elements! + this.transform(mctx); + this.painters(mctx); + this.each(function(child) { + child.render(mctx); + }); + + mctx.restore(); +}; + +Element.prototype._renderAsMasked = function(ctx) { + // FIXME: the complete mask process should be a Painter. + var mask = this.$mask; + + var ratio = engine.PX_RATIO; + + if (!mask.__maskCvs) throw new Error('Mask canvas is not ready!'); + + var mcvs = mask.__maskCvs, + bcvs = mask.__backCvs, + bctx = mask.__backCtx; + + var maskSize = mask._maskCvsSize, + maskPos = mask._maskCvsPos; + + var width = maskSize[0], + height = maskSize[1], + x = maskPos[0], y = maskPos[1]; + + var scale = ratio; // multiple by global scale when it's known + + bctx.clearRect(0, 0, width*scale, height*scale); + + bctx.save(); + bctx.setTransform(scale, 0, 0, scale, -x*scale, -y*scale); + + this.transform(bctx); + this.painters(bctx); + this.each(function(child) { + child.render(bctx); + }); + + bctx.globalCompositeOperation = 'destination-in'; + bctx.setTransform(1, 0, 0, 1, 0, 0); + bctx.drawImage(mcvs, 0, 0); + + ctx.drawImage(bcvs, + 0, 0, Math.floor(width * scale), Math.floor(height * scale), + x, y, width, height); + + bctx.restore(); }; /**