Source: fill/image.js

goog.provide('lime.fill.Image');

goog.require('lime.fill.Fill');

/**
 * Image fill.
 * @param {string|Image|lime.Sprite} img Image.
 * @constructor
 * @extends lime.fill.Fill
 */
lime.fill.Image = function(img) {
    lime.fill.Fill.call(this);

    /*if ((img instanceof lime.Sprite)) {
        sprite.setRenderMode( lime.RenderMode.BACKGROUND_CANVAS );
        this.image_ = img;
        goog.events.listenOnce(this.image_.eventTarget, 'update',
            this.updateHandler_, false, this);
    }
    else */

    if (img && goog.isFunction(img.data)) {
        img = img.data();
    }

    if (goog.isString(img)) {
        this.url_ = img;
        if (this.url_.length > 50)
            this.url_ = this.url_.substr(-50);
        if (lime.fill.Image.loadedImages_[this.url_]) {
            this.image_ = lime.fill.Image.loadedImages_[this.url_];
        } else {
            this.image_ = new Image();
            this.image_.src = img;
        }
    } else {
        this.url_ = img.src;
        if (this.url_.length > 50)
            this.url_ = this.url_.substr(-50);
        if (lime.fill.Image.loadedImages_[this.url_]) {
            this.image_ = lime.fill.Image.loadedImages_[this.url_];
        } else {
            this.image_ = img;
        }

    }

    if (!this.isLoaded()) {
        this.addLoadHandler_();
    }

    lime.fill.Image.loadedImages_[this.url_] = this.image_;


};
goog.inherits(lime.fill.Image, lime.fill.Fill);

/**
 * Already loaded image cache to reuse all img objects.
 * @private
 */
lime.fill.Image.loadedImages_ = {};

/**
 * Common name for Image objects
 * @type {string}
 */
lime.fill.Image.prototype.id = 'image';

/**
 * @inheritDoc
 */
lime.fill.Image.prototype.initForSprite = function(sprite) {
    var size = sprite.getSize(),
        that = this;
    if (!size.width && !size.height) {
        if (!this.isLoaded()) {

            goog.events.listen(this, goog.events.EventType.LOAD, function() {
                var size = this.getSize();
                if (!size.width && !size.height) {
                    this.setSize(that.image_.width, that.image_.height);
                }
            }, false, sprite);

        } else {

            sprite.setSize(this.image_.width, this.image_.height);

        }
    }

    if (!this.isLoaded()) {
        goog.events.listen(this, goog.events.EventType.LOAD, function() {
            sprite.setDirty(lime.Dirty.CONTENT);
        }, false, this);
    }
};

/**
 * @private
 */
lime.fill.Image.prototype.addLoadHandler_ = function() {
    goog.events.listen(this.image_, goog.events.EventType.LOAD,
        this.imageLoadedHandler_, false, this);
}

/**
 * Update sprite dimensions after image has been loaded
 * @param {Event} e Event.
 * @private
 */
lime.fill.Image.prototype.imageLoadedHandler_ = function(e) {
    this.dispatchEvent(e);
};

/**
 * Return core DOM Image element for the fill.
 * @return {HTMLImageElement} Image element.
 */
lime.fill.Image.prototype.getImageElement = function() {
    return this.image_;
};

/**
 * Return true if image object has been loaded from network.
 * @return {boolean} If image has been loaded.
 */
lime.fill.Image.prototype.isLoaded = function() {
    return !!(this.image_ && this.image_.width && this.image_.height);
}

/**
 * Set the drawing size for the fill. Size can also be passed in
 * with two numbers.
 * @param {(goog.math.Size|number)} size Image fill size.
 * @param {(boolean|number)=} opt_perc If size is relative factor from original.
 * @return {lime.fill.Image} object itself.
 */
lime.fill.Image.prototype.setSize = function(size, opt_perc) {
    if (goog.isNumber(size)) {
        size = new goog.math.Size(arguments[0], arguments[1]);
        opt_perc = arguments[2] || false;
    }
    this.size_ = size;
    this.size_perc_ = opt_perc;
    return this;
}

/**
 * Set the offset that defines the drawing start position. Default is top-left(0,0).
 * @param {goog.math.Coordinate} offset Image fill offset.
 * @param {boolean=} opt_perc If offset is relative factor from size.
 * @return {lime.fill.Image} object itself.
 */
lime.fill.Image.prototype.setOffset = function(offset, opt_perc) {
    if (goog.isNumber(offset)) {
        offset = new goog.math.Coordinate(arguments[0], arguments[1]);
        opt_perc = arguments[2] || false;
    }
    this.offset_ = offset;
    this.offset_perc_ = opt_perc;
    return this;
}

lime.fill.Image.prototype.getPixelSizeAndOffset = function(shape) {
    var size = shape.getSize().clone();
    if (this.size_) {
        if (this.size_perc_) {
            size.width *= this.size_.width;
            size.height *= this.size_.height;
        } else {
            size = this.size_;
        }
    }
    var offset = new goog.math.Coordinate(0, 0);
    if (this.offset_) {
        if (this.offset_perc_) {
            offset.x = size.width * this.offset_.x;
            offset.y = size.height * this.offset_.y;
        } else {
            offset = this.offset_;
        }
    }
    return [size, offset];
}


/**
 * Common functionality so it could be reused on Frame
 * @protected
 */
lime.fill.Image.prototype.setDOMBackgroundProp_ = function(domEl, shape) {
    var so = this.getPixelSizeAndOffset(shape),
        size = so[0],
        offset = so[1],
        q = shape.getRelativeQuality();
    domEl.style[lime.style.getCSSproperty('BackgroundSize')] = size.width * q + 'px ' + size.height * q + 'px';
    var stroke = shape.stroke_ ? shape.stroke_.width_ : 0;
    domEl.style['backgroundPosition'] = (offset.x * q - stroke) + 'px ' + (offset.y * q - stroke) + 'px';
    //domEl.style['backgroundRepeat'] = 'no-repeat';
    if (this.qualityRenderer)
        domEl.style['imageRendering'] = 'optimizeQuality';
}

/** @inheritDoc */
lime.fill.Image.prototype.setDOMStyle = function(domEl, shape) {
    domEl.style['background'] = 'url(' + this.image_.src + ')';
    this.setDOMBackgroundProp_(domEl, shape);
};

lime.fill.Image.prototype.setCanvasStyle = function(context, shape) {
    var size = shape.getSize(),
        frame = shape.getFrame();
    if (!size.width || !size.height) return;
    try {
        var img = this.getImageElement();
        var so = this.getPixelSizeAndOffset(shape),
            s = so[0],
            offset = so[1];
        /* todo: No idea if drawimage() with loops is faster or if the
           pattern object needs to be cached. Needs to be tested! */
        var ptrn = context.createPattern(img, 'repeat');
        var aspx = s.width / img.width,
            aspy = s.height / img.height;
        context.save();
        context.translate(frame.left + offset.x, frame.top + offset.y);
        context.scale(aspx, aspy);
        context.fillStyle = ptrn;
        context.fillRect(-offset.x / aspx, -offset.y / aspy, size.width / aspx, size.height / aspy);
        context.restore();
    } catch (e) {}
};