Source: gfx/core/renderers/canvas/CanvasRenderer.js

const SystemRenderer = require('../SystemRenderer');
const CanvasMaskManager = require('./utils/CanvasMaskManager');
const utils = require('../../utils');
const math = require('../../math');
const CONST = require('../../../const');

/**
 * The CanvasRenderer draws the scene and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
 * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :)
 *
 * @class
 * @extends SystemRenderer
 * @param [width=800] {number} the width of the canvas view
 * @param [height=600] {number} the height of the canvas view
 * @param [options] {object} The optional renderer parameters
 * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional
 * @param [options.transparent=false] {boolean} If the render view is transparent, default false
 * @param [options.autoResize=false] {boolean} If the render view is automatically resized, default false
 * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment)
 * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2
 * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or
 *      not before the new render pass.
 * @param [options.roundPixels=false] {boolean} If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
 */
class CanvasRenderer extends SystemRenderer {
  constructor(width, height, options = {}) {
    super('Canvas', width, height, options);

    this.type = CONST.RENDERER_TYPE.CANVAS;

    /**
     * The canvas 2d context that everything is drawn with.
     *
     * @member {CanvasRenderingContext2D}
     */
    this.context = this.view.getContext('2d', { alpha: this.transparent });

    /**
     * Boolean flag controlling canvas refresh.
     *
     * @member {boolean}
     */
    this.refresh = true;

    /**
     * Instance of a CanvasMaskManager, handles masking when using the canvas renderer.
     *
     * @member {PIXI.CanvasMaskManager}
     */
    this.maskManager = new CanvasMaskManager();

    /**
     * The canvas property used to set the canvas smoothing property.
     *
     * @member {string}
     */
    this.smoothProperty = 'imageSmoothingEnabled';

    if (!this.context.imageSmoothingEnabled) {
      if (this.context.webkitImageSmoothingEnabled) {
        this.smoothProperty = 'webkitImageSmoothingEnabled';
      }
      else if (this.context.mozImageSmoothingEnabled) {
        this.smoothProperty = 'mozImageSmoothingEnabled';
      }
      else if (this.context.oImageSmoothingEnabled) {
        this.smoothProperty = 'oImageSmoothingEnabled';
      }
      else if (this.context.msImageSmoothingEnabled) {
        this.smoothProperty = 'msImageSmoothingEnabled';
      }
    }

    this.initPlugins();

    this._mapBlendModes();

    /**
     * This temporary display object used as the parent of the currently being rendered item
     *
     * @member {PIXI.DisplayObject}
     * @private
     */
    this._tempDisplayObjectParent = {
      worldTransform: new math.Matrix(),
      worldAlpha: 1,
    };


    this.resize(width, height);
  }
}

utils.pluginTarget.mixin(CanvasRenderer);

/**
 * Renders the object to this canvas view
 *
 * @param object {PIXI.DisplayObject} the object to be rendered
 */
CanvasRenderer.prototype.render = function(object) {
  this.emit('prerender');

  var cacheParent = object.parent;

  this._lastObjectRendered = object;

  object.parent = this._tempDisplayObjectParent;

    // update the scene graph
  object.updateTransform();

  object.parent = cacheParent;

  this.context.setTransform(1, 0, 0, 1, 0, 0);

  this.context.globalAlpha = 1;

  this.context.globalCompositeOperation = this.blendModes[CONST.BLEND_MODES.NORMAL];

  if (navigator.isCocoonJS && this.view.screencanvas) {
    this.context.fillStyle = 'black';
    this.context.clear();
  }

  if (this.clearBeforeRender) {
    if (this.transparent) {
      this.context.clearRect(0, 0, this.width, this.height);
    }
    else {
      this.context.fillStyle = this._backgroundColorString;
      this.context.fillRect(0, 0, this.width , this.height);
    }
  }

  this.renderDisplayObject(object, this.context);

  this.emit('postrender');
};

/**
 * Removes everything from the renderer and optionally removes the Canvas DOM element.
 *
 * @param [removeView=false] {boolean} Removes the Canvas element from the DOM.
 */
CanvasRenderer.prototype.destroy = function(removeView) {
  this.destroyPlugins();

    // call the base destroy
  SystemRenderer.prototype.destroy.call(this, removeView);

  this.context = null;

  this.refresh = true;

  this.maskManager.destroy();
  this.maskManager = null;

  this.smoothProperty = null;
};

/**
 * Renders a display object
 *
 * @param displayObject {PIXI.DisplayObject} The displayObject to render
 * @private
 */
CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) {
  var tempContext = this.context;

  this.context = context;
  displayObject.renderCanvas(this);
  this.context = tempContext;
};

/**
 * @extends PIXI.SystemRenderer#resize
 *
 * @param {number} w
 * @param {number} h
 */
CanvasRenderer.prototype.resize = function(w, h) {
  SystemRenderer.prototype.resize.call(this, w, h);

    // reset the scale mode.. oddly this seems to be reset when the canvas is resized.
    // surely a browser bug?? Let pixi fix that for you..
  if (this.smoothProperty) {
    this.context[this.smoothProperty] = (CONST.SCALE_MODES.DEFAULT === CONST.SCALE_MODES.LINEAR);
  }

};

/**
 * Maps Pixi blend modes to canvas blend modes.
 *
 * @private
 */
CanvasRenderer.prototype._mapBlendModes = function() {
  if (!this.blendModes) {
    this.blendModes = {};

    if (utils.canUseNewCanvasBlendModes()) {
      this.blendModes[CONST.BLEND_MODES.NORMAL] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.ADD] = 'lighter'; // IS THIS OK???
      this.blendModes[CONST.BLEND_MODES.MULTIPLY] = 'multiply';
      this.blendModes[CONST.BLEND_MODES.SCREEN] = 'screen';
      this.blendModes[CONST.BLEND_MODES.OVERLAY] = 'overlay';
      this.blendModes[CONST.BLEND_MODES.DARKEN] = 'darken';
      this.blendModes[CONST.BLEND_MODES.LIGHTEN] = 'lighten';
      this.blendModes[CONST.BLEND_MODES.COLOR_DODGE] = 'color-dodge';
      this.blendModes[CONST.BLEND_MODES.COLOR_BURN] = 'color-burn';
      this.blendModes[CONST.BLEND_MODES.HARD_LIGHT] = 'hard-light';
      this.blendModes[CONST.BLEND_MODES.SOFT_LIGHT] = 'soft-light';
      this.blendModes[CONST.BLEND_MODES.DIFFERENCE] = 'difference';
      this.blendModes[CONST.BLEND_MODES.EXCLUSION] = 'exclusion';
      this.blendModes[CONST.BLEND_MODES.HUE] = 'hue';
      this.blendModes[CONST.BLEND_MODES.SATURATION] = 'saturate';
      this.blendModes[CONST.BLEND_MODES.COLOR] = 'color';
      this.blendModes[CONST.BLEND_MODES.LUMINOSITY] = 'luminosity';
    }
    else {
            // this means that the browser does not support the cool new blend modes in canvas 'cough' ie 'cough'
      this.blendModes[CONST.BLEND_MODES.NORMAL] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.ADD] = 'lighter'; // IS THIS OK???
      this.blendModes[CONST.BLEND_MODES.MULTIPLY] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.SCREEN] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.OVERLAY] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.DARKEN] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.LIGHTEN] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.COLOR_DODGE] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.COLOR_BURN] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.HARD_LIGHT] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.SOFT_LIGHT] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.DIFFERENCE] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.EXCLUSION] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.HUE] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.SATURATION] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.COLOR] = 'source-over';
      this.blendModes[CONST.BLEND_MODES.LUMINOSITY] = 'source-over';
    }
  }
};

module.exports = CanvasRenderer;