Source: renderer/canvas.js

goog.provide('lime.Renderer.CANVAS');

goog.require('goog.math.Box.size');
goog.require('goog.math.Size.scaleVec2');
goog.require('lime.Renderer');

/**
 * Canvas renderer. This renders as canvas element or just
 * draws on parent context.
 * @const
 * @type {lime.Renderer}
 */
lime.Renderer.CANVAS = new lime.Renderer();

/**
 * Update the DOM tree relations of the element.
 * @this {lime.Node}
 */
lime.Renderer.CANVAS.updateLayout = function() {};

/**
 * Initalize canvas element and start the drawing process
 * @this {lime.Node}
 */
lime.Renderer.CANVAS.drawCanvas = function() {
    var quality = this.getQuality(),
        bounds = this.measureContents(),
        rquality = this.relativeQuality_ || 1,
        ownquality = rquality / quality,
        sizediff,
        PADDING = 12;


    if (!this.domElement) return;

    if (this.boundsCache && this.boundsCache.contains(bounds) &&
        (sizediff = this.boundsCache.size().area() / bounds.size().area()) &&
        sizediff < 1.6 && sizediff > 0.5) {
        //use the cached canvas size
        bounds = this.boundsCache;
    } else {
        if (this.staticCanvas != 1 && this.children_.length != 0) {
            if (!(this instanceof lime.Scene)) {
                bounds.expand(PADDING, PADDING, PADDING, PADDING);
            }
        }
    }


    //clip to director
    /* var dir = this.getDirector();
    if (this.rotation_ == 0 && dir) {
    var fr = dir.getFrame();
    var br = dir.localToNode(
    new goog.math.Coordinate(fr.right, fr.bottom), this);
    var tl = dir.localToNode(
    new goog.math.Coordinate(fr.left, fr.top), this);
    if (br.x < bounds.right) {
    bounds.right = br.x;
    }
    if (br.y < bounds.bottom) {
    bounds.bottom = br.y;
    }
    if (tl.x > bounds.left) {
    bounds.left = tl.x;
    }
    if (tl.y > bounds.top) {
    bounds.top = tl.y;
    }
    }*/

    this.boundsCache = bounds; //save for later use

    var bsize = bounds.size();
    var pxsize = bsize.clone().scale(rquality).ceil();

    if (this.domElement.width != pxsize.width ||
        this.domElement.height != pxsize.height) {
        this.domElement.width = pxsize.width;
        this.domElement.height = pxsize.height;
        this.redraw_ = 1;
        //   console.log('redraw');
    }



    var realScale = this.getScale().clone();
    if (this.transitionsActive_[lime.Transition.SCALE]) {
        realScale = this.transitionsActive_[lime.Transition.SCALE];
        //this.redraw_ = 1;
    }
    if (pxsize.width != 0) {
        realScale.scale(bsize.width * ownquality / pxsize.width);
    } else {
        realScale.scale(1 / quality);
    }


    var fr = this.getFrame();
    this.ax = (fr.left - bounds.left) * rquality;
    this.ay = (fr.top - bounds.top) * rquality;


    var ap_offset = this.getSize().clone().
    scaleVec2(this.getAnchorPoint()).scale(rquality);

    var pos = this.getPosition().clone();

    if (this.transitionsActive_[lime.Transition.POSITION]) {
        pos = this.transitionsActive_[lime.Transition.POSITION];
        //this.redraw_ = 1;
    }

    pos.x *= ownquality;
    pos.y *= ownquality;

    pos.x -= ap_offset.width + this.ax;
    pos.y -= ap_offset.height + this.ay;

    lime.style.setTransformOrigin(this.domElement, (this.ax + ap_offset.width) / pxsize.width * 100, (this.ay + ap_offset.height) / pxsize.height * 100, true);

    if (!this.transitionsActiveSet_[lime.Transition.POSITION] && !this.transitionsActiveSet_[lime.Transition.SCALE] && !this.transitionsActiveSet_[lime.Transition.ROTATION]) {

        var rotation = -this.getRotation();
        if (goog.isDef(this.transitionsActive_[lime.Transition.ROTATION])) {
            rotation = -this.transitionsActive_[lime.Transition.ROTATION];
        }
        lime.style.setTransform(this.domElement,
            new lime.style.Transform().setPrecision(.1).translate(pos.x, pos.y).scale(realScale.x, realScale.y).rotate(rotation));
    }

    if (this.redraw_) {
        var context = this.domElement.getContext('2d');
        rquality = this.relativeQuality_ || 1;

        context.clearRect(0, 0, pxsize.width, pxsize.height);
        context.save();
        context.translate(this.ax, this.ay);
        context.scale(rquality, rquality);


        var size = this.getSize(),
            anchor = this.getAnchorPoint();

        context.translate(size.width * anchor.x, size.height * anchor.y);

        this.renderer.drawCanvasObject.call(this, context);

        context.restore();
        this.redraw_ = 0;


    }
};

/**
 * Update nodes dirty values and call draw after
 * @this {lime.Node}
 */
lime.Renderer.CANVAS.update = function() {};


/**
 * Draw single object to the canvas context
 * @param {Object} context Canvas2DContext where to draw.
 * @this {lime.Node}
 */
lime.Renderer.CANVAS.drawCanvasObject = function(context) {

    if (!this.inTree_) return;

    if (this.mask_ != this.activeMask_) {
        if (this.activeMask_) {
            lime.Renderer.DOM.removeMask.call(this);
        }

        if (this.mask_) {
            lime.Renderer.DOM.addMask.call(this);
        }
    }

    // if element is mask the only update mask prop and return
    if (this.maskTarget_) {
        return;
    }

    if (this.hidden_ || this.opacity_ == 0 || this.isMask == 1) {
        return;
    }

    if (this.opacity_ != 1) {
        context.globalAlpha *= this.opacity_;
    }

    if (this.mask_) {
        lime.Renderer.DOM.calculateMaskPosition.call(this.mask_);
        var m = this.activeMask_,
            scale = this.scale_;
        context.save();
        context.save();
        context.translate(m.mPos.x, m.mPos.y);
        context.rotate(-m.mRot);
        if (this.needsDomElement) {
            context.rotate(this.getRotation() * Math.PI / 180);
        }
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(m.mWidth / scale.x, 0);
        context.lineTo(m.mWidth / scale.x, m.mHeight / scale.y);
        context.lineTo(0, m.mHeight / scale.y);
        context.closePath();
        context.restore();
        context.clip();
    }


    var zero = new goog.math.Coordinate(0, 0);

    this.renderer.draw.call(this, context);

    for (var i = 0, child; child = this.children_[i]; i++) {
        var pos = child.localToParent(zero).clone(),
            rot = child.getRotation(),
            scale = child.getScale();
        context.save();
        context.translate(pos.x, pos.y);
        context.scale(scale.x, scale.y);

        if (rot != 0) {
            context.rotate(-rot * Math.PI / 180);
        }
        this.renderer.drawCanvasObject.call(child, context);
        context.restore();

    }

    if (this.opacity_ != 1) {
        context.globalAlpha /= this.opacity_;
    }
    if (this.activeMask_) {
        context.restore();
    }

};