diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/canvas/CanvasGraphicsRenderer.js b/src/core/graphics/canvas/CanvasGraphicsRenderer.js new file mode 100644 index 0000000..024402d --- /dev/null +++ b/src/core/graphics/canvas/CanvasGraphicsRenderer.js @@ -0,0 +1,250 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasGraphicsRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasGraphicsRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasGraphicsRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasGraphicsRenderer.prototype.constructor = CanvasGraphicsRenderer; +module.exports = CanvasGraphicsRenderer; + +CanvasRenderer.registerPlugin('graphics', CanvasGraphicsRenderer); + +/* + * Renders a Graphics object to a canvas. + * + * @param graphics {PIXI.Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +CanvasGraphicsRenderer.prototype.render = function (graphics) +{ + var renderer = this.renderer; + var context = renderer.context; + var worldAlpha = graphics.worldAlpha; + + if (graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if (data.type === CONST.SHAPES.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if (shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RECT) + { + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if (data.type === CONST.SHAPES.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Updates the tint of a graphics object + * + * @private + * @param graphics {PIXI.Graphics} the graphics that will have its tint updated + * + */ +CanvasGraphicsRenderer.prototype.updateGraphicsTint = function (graphics) +{ + if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) + { + return; + } + + graphics._prevTint = graphics.tint; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + } +}; + diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/canvas/CanvasGraphicsRenderer.js b/src/core/graphics/canvas/CanvasGraphicsRenderer.js new file mode 100644 index 0000000..024402d --- /dev/null +++ b/src/core/graphics/canvas/CanvasGraphicsRenderer.js @@ -0,0 +1,250 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasGraphicsRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasGraphicsRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasGraphicsRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasGraphicsRenderer.prototype.constructor = CanvasGraphicsRenderer; +module.exports = CanvasGraphicsRenderer; + +CanvasRenderer.registerPlugin('graphics', CanvasGraphicsRenderer); + +/* + * Renders a Graphics object to a canvas. + * + * @param graphics {PIXI.Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +CanvasGraphicsRenderer.prototype.render = function (graphics) +{ + var renderer = this.renderer; + var context = renderer.context; + var worldAlpha = graphics.worldAlpha; + + if (graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if (data.type === CONST.SHAPES.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if (shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RECT) + { + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if (data.type === CONST.SHAPES.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Updates the tint of a graphics object + * + * @private + * @param graphics {PIXI.Graphics} the graphics that will have its tint updated + * + */ +CanvasGraphicsRenderer.prototype.updateGraphicsTint = function (graphics) +{ + if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) + { + return; + } + + graphics._prevTint = graphics.tint; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + } +}; + diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 840560c..761b22c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -83,16 +83,7 @@ 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); @@ -109,47 +100,54 @@ * * @param object {PIXI.DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (object) +CanvasRenderer.prototype.render = function (displayObject, renderTexture, clear, transform, skipUpdateTransform) { + var context = this.context; + this.emit('prerender'); - var cacheParent = object.parent; + this._lastObjectRendered = displayObject; - this._lastObjectRendered = object; + if(!skipUpdateTransform) + { + // update the scene graph + var cacheParent = displayObject.parent; + displayObject.parent = this._tempDisplayObjectParent; + displayObject.updateTransform(); + displayObject.parent = cacheParent; + // displayObject.hitArea = //TODO add a temp hit area + } - 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]; + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalAlpha = 1; + context.globalCompositeOperation = this.blendModes[CONST.BLEND_MODES.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { - this.context.fillStyle = 'black'; - this.context.clear(); + context.fillStyle = 'black'; + context.clear(); } - if (this.clearBeforeRender) + if( clear || this.clearBeforeRender) { if (this.transparent) { - this.context.clearRect(0, 0, this.width, this.height); + context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = this._backgroundColorString; - this.context.fillRect(0, 0, this.width , this.height); + context.fillStyle = this._backgroundColorString; + context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(object, this.context); + // TODO RENDER TARGET STUFF HERE.. + var tempContext = this.context; + + this.context = context; + displayObject.renderCanvas(this); + this.context = tempContext; this.emit('postrender'); }; @@ -177,21 +175,6 @@ }; /** - * 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 diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/canvas/CanvasGraphicsRenderer.js b/src/core/graphics/canvas/CanvasGraphicsRenderer.js new file mode 100644 index 0000000..024402d --- /dev/null +++ b/src/core/graphics/canvas/CanvasGraphicsRenderer.js @@ -0,0 +1,250 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasGraphicsRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasGraphicsRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasGraphicsRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasGraphicsRenderer.prototype.constructor = CanvasGraphicsRenderer; +module.exports = CanvasGraphicsRenderer; + +CanvasRenderer.registerPlugin('graphics', CanvasGraphicsRenderer); + +/* + * Renders a Graphics object to a canvas. + * + * @param graphics {PIXI.Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +CanvasGraphicsRenderer.prototype.render = function (graphics) +{ + var renderer = this.renderer; + var context = renderer.context; + var worldAlpha = graphics.worldAlpha; + + if (graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if (data.type === CONST.SHAPES.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if (shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RECT) + { + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if (data.type === CONST.SHAPES.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Updates the tint of a graphics object + * + * @private + * @param graphics {PIXI.Graphics} the graphics that will have its tint updated + * + */ +CanvasGraphicsRenderer.prototype.updateGraphicsTint = function (graphics) +{ + if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) + { + return; + } + + graphics._prevTint = graphics.tint; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + } +}; + diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 840560c..761b22c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -83,16 +83,7 @@ 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); @@ -109,47 +100,54 @@ * * @param object {PIXI.DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (object) +CanvasRenderer.prototype.render = function (displayObject, renderTexture, clear, transform, skipUpdateTransform) { + var context = this.context; + this.emit('prerender'); - var cacheParent = object.parent; + this._lastObjectRendered = displayObject; - this._lastObjectRendered = object; + if(!skipUpdateTransform) + { + // update the scene graph + var cacheParent = displayObject.parent; + displayObject.parent = this._tempDisplayObjectParent; + displayObject.updateTransform(); + displayObject.parent = cacheParent; + // displayObject.hitArea = //TODO add a temp hit area + } - 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]; + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalAlpha = 1; + context.globalCompositeOperation = this.blendModes[CONST.BLEND_MODES.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { - this.context.fillStyle = 'black'; - this.context.clear(); + context.fillStyle = 'black'; + context.clear(); } - if (this.clearBeforeRender) + if( clear || this.clearBeforeRender) { if (this.transparent) { - this.context.clearRect(0, 0, this.width, this.height); + context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = this._backgroundColorString; - this.context.fillRect(0, 0, this.width , this.height); + context.fillStyle = this._backgroundColorString; + context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(object, this.context); + // TODO RENDER TARGET STUFF HERE.. + var tempContext = this.context; + + this.context = context; + displayObject.renderCanvas(this); + this.context = tempContext; this.emit('postrender'); }; @@ -177,21 +175,6 @@ }; /** - * 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 diff --git a/src/core/renderers/canvas/utils/CanvasGraphics.js b/src/core/renderers/canvas/utils/CanvasGraphics.js deleted file mode 100644 index b2d5b91..0000000 --- a/src/core/renderers/canvas/utils/CanvasGraphics.js +++ /dev/null @@ -1,352 +0,0 @@ -var CONST = require('../../../const'); - -/** - * A set of functions used by the canvas renderer to draw the primitive graphics data. - * @static - * @class - * @memberof PIXI - */ -var CanvasGraphics = {}; -module.exports = CanvasGraphics; - -/* - * Renders a Graphics object to a canvas. - * - * @param graphics {PIXI.Graphics} the actual graphics object to render - * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas - */ -CanvasGraphics.renderGraphics = function (graphics, context) -{ - var worldAlpha = graphics.worldAlpha; - - if (graphics.dirty) - { - this.updateGraphicsTint(graphics); - graphics.dirty = false; - } - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - var fillColor = data._fillTint; - var lineColor = data._lineTint; - - context.lineWidth = data.lineWidth; - - if (data.type === CONST.SHAPES.POLY) - { - context.beginPath(); - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - if (shape.closed) - { - context.lineTo(points[0], points[1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RECT) - { - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fillRect(shape.x, shape.y, shape.width, shape.height); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.strokeRect(shape.x, shape.y, shape.width, shape.height); - } - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.beginPath(); - context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.ELIP) - { - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - context.beginPath(); - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RREC) - { - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - } -}; - -/* - * Renders a graphics mask - * - * @private - * @param graphics {PIXI.Graphics} the graphics which will be used as a mask - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas - */ -CanvasGraphics.renderGraphicsMask = function (graphics, context) -{ - var len = graphics.graphicsData.length; - - if (len === 0) - { - return; - } - - context.beginPath(); - - for (var i = 0; i < len; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - if (data.type === CONST.SHAPES.POLY) - { - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - } - else if (data.type === CONST.SHAPES.RECT) - { - context.rect(shape.x, shape.y, shape.width, shape.height); - context.closePath(); - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.arc(shape.x, shape.y, shape.radius, 0, 2 * Math.PI); - context.closePath(); - } - else if (data.type === CONST.SHAPES.ELIP) - { - - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - } - else if (data.type === CONST.SHAPES.RREC) - { - - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - } - } -}; - -/* - * Updates the tint of a graphics object - * - * @private - * @param graphics {PIXI.Graphics} the graphics that will have its tint updated - * - */ -CanvasGraphics.updateGraphicsTint = function (graphics) -{ - if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) - { - return; - } - graphics._prevTint = graphics.tint; - - var tintR = (graphics.tint >> 16 & 0xFF) / 255; - var tintG = (graphics.tint >> 8 & 0xFF) / 255; - var tintB = (graphics.tint & 0xFF)/ 255; - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - - var fillColor = data.fillColor | 0; - var lineColor = data.lineColor | 0; - - /* - var colorR = (fillColor >> 16 & 0xFF) / 255; - var colorG = (fillColor >> 8 & 0xFF) / 255; - var colorB = (fillColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - - colorR = (lineColor >> 16 & 0xFF) / 255; - colorG = (lineColor >> 8 & 0xFF) / 255; - colorB = (lineColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - */ - - // super inline cos im an optimization NAZI :) - data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); - data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); - - } -}; - diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/canvas/CanvasGraphicsRenderer.js b/src/core/graphics/canvas/CanvasGraphicsRenderer.js new file mode 100644 index 0000000..024402d --- /dev/null +++ b/src/core/graphics/canvas/CanvasGraphicsRenderer.js @@ -0,0 +1,250 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasGraphicsRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasGraphicsRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasGraphicsRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasGraphicsRenderer.prototype.constructor = CanvasGraphicsRenderer; +module.exports = CanvasGraphicsRenderer; + +CanvasRenderer.registerPlugin('graphics', CanvasGraphicsRenderer); + +/* + * Renders a Graphics object to a canvas. + * + * @param graphics {PIXI.Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +CanvasGraphicsRenderer.prototype.render = function (graphics) +{ + var renderer = this.renderer; + var context = renderer.context; + var worldAlpha = graphics.worldAlpha; + + if (graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if (data.type === CONST.SHAPES.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if (shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RECT) + { + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if (data.type === CONST.SHAPES.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Updates the tint of a graphics object + * + * @private + * @param graphics {PIXI.Graphics} the graphics that will have its tint updated + * + */ +CanvasGraphicsRenderer.prototype.updateGraphicsTint = function (graphics) +{ + if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) + { + return; + } + + graphics._prevTint = graphics.tint; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + } +}; + diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 840560c..761b22c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -83,16 +83,7 @@ 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); @@ -109,47 +100,54 @@ * * @param object {PIXI.DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (object) +CanvasRenderer.prototype.render = function (displayObject, renderTexture, clear, transform, skipUpdateTransform) { + var context = this.context; + this.emit('prerender'); - var cacheParent = object.parent; + this._lastObjectRendered = displayObject; - this._lastObjectRendered = object; + if(!skipUpdateTransform) + { + // update the scene graph + var cacheParent = displayObject.parent; + displayObject.parent = this._tempDisplayObjectParent; + displayObject.updateTransform(); + displayObject.parent = cacheParent; + // displayObject.hitArea = //TODO add a temp hit area + } - 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]; + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalAlpha = 1; + context.globalCompositeOperation = this.blendModes[CONST.BLEND_MODES.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { - this.context.fillStyle = 'black'; - this.context.clear(); + context.fillStyle = 'black'; + context.clear(); } - if (this.clearBeforeRender) + if( clear || this.clearBeforeRender) { if (this.transparent) { - this.context.clearRect(0, 0, this.width, this.height); + context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = this._backgroundColorString; - this.context.fillRect(0, 0, this.width , this.height); + context.fillStyle = this._backgroundColorString; + context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(object, this.context); + // TODO RENDER TARGET STUFF HERE.. + var tempContext = this.context; + + this.context = context; + displayObject.renderCanvas(this); + this.context = tempContext; this.emit('postrender'); }; @@ -177,21 +175,6 @@ }; /** - * 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 diff --git a/src/core/renderers/canvas/utils/CanvasGraphics.js b/src/core/renderers/canvas/utils/CanvasGraphics.js deleted file mode 100644 index b2d5b91..0000000 --- a/src/core/renderers/canvas/utils/CanvasGraphics.js +++ /dev/null @@ -1,352 +0,0 @@ -var CONST = require('../../../const'); - -/** - * A set of functions used by the canvas renderer to draw the primitive graphics data. - * @static - * @class - * @memberof PIXI - */ -var CanvasGraphics = {}; -module.exports = CanvasGraphics; - -/* - * Renders a Graphics object to a canvas. - * - * @param graphics {PIXI.Graphics} the actual graphics object to render - * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas - */ -CanvasGraphics.renderGraphics = function (graphics, context) -{ - var worldAlpha = graphics.worldAlpha; - - if (graphics.dirty) - { - this.updateGraphicsTint(graphics); - graphics.dirty = false; - } - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - var fillColor = data._fillTint; - var lineColor = data._lineTint; - - context.lineWidth = data.lineWidth; - - if (data.type === CONST.SHAPES.POLY) - { - context.beginPath(); - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - if (shape.closed) - { - context.lineTo(points[0], points[1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RECT) - { - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fillRect(shape.x, shape.y, shape.width, shape.height); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.strokeRect(shape.x, shape.y, shape.width, shape.height); - } - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.beginPath(); - context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.ELIP) - { - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - context.beginPath(); - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RREC) - { - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - } -}; - -/* - * Renders a graphics mask - * - * @private - * @param graphics {PIXI.Graphics} the graphics which will be used as a mask - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas - */ -CanvasGraphics.renderGraphicsMask = function (graphics, context) -{ - var len = graphics.graphicsData.length; - - if (len === 0) - { - return; - } - - context.beginPath(); - - for (var i = 0; i < len; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - if (data.type === CONST.SHAPES.POLY) - { - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - } - else if (data.type === CONST.SHAPES.RECT) - { - context.rect(shape.x, shape.y, shape.width, shape.height); - context.closePath(); - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.arc(shape.x, shape.y, shape.radius, 0, 2 * Math.PI); - context.closePath(); - } - else if (data.type === CONST.SHAPES.ELIP) - { - - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - } - else if (data.type === CONST.SHAPES.RREC) - { - - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - } - } -}; - -/* - * Updates the tint of a graphics object - * - * @private - * @param graphics {PIXI.Graphics} the graphics that will have its tint updated - * - */ -CanvasGraphics.updateGraphicsTint = function (graphics) -{ - if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) - { - return; - } - graphics._prevTint = graphics.tint; - - var tintR = (graphics.tint >> 16 & 0xFF) / 255; - var tintG = (graphics.tint >> 8 & 0xFF) / 255; - var tintB = (graphics.tint & 0xFF)/ 255; - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - - var fillColor = data.fillColor | 0; - var lineColor = data.lineColor | 0; - - /* - var colorR = (fillColor >> 16 & 0xFF) / 255; - var colorG = (fillColor >> 8 & 0xFF) / 255; - var colorB = (fillColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - - colorR = (lineColor >> 16 & 0xFF) / 255; - colorG = (lineColor >> 8 & 0xFF) / 255; - colorB = (lineColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - */ - - // super inline cos im an optimization NAZI :) - data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); - data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); - - } -}; - diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 9e46aed..7e65e3e 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -270,6 +270,18 @@ }; /** +* Renders the object using the Canvas renderer +* +* @param renderer {PIXI.CanvasRenderer} The renderer +* @private +*/ +Sprite.prototype._renderCanvas = function (renderer) +{ + renderer.plugins.sprite.render(this); +}; + + +/** * Returns the bounds of the Sprite as a rectangle. The bounds calculation takes the worldTransform into account. * * @param matrix {PIXI.Matrix} the transformation matrix of the sprite @@ -400,134 +412,6 @@ return false; }; -/** -* Renders the object using the Canvas renderer -* -* @param renderer {PIXI.CanvasRenderer} The renderer -* @private -*/ -Sprite.prototype._renderCanvas = function (renderer) -{ - if (this.texture.crop.width <= 0 || this.texture.crop.height <= 0) - { - return; - } - - var compositeOperation = renderer.blendModes[this.blendMode]; - if (compositeOperation !== renderer.context.globalCompositeOperation) - { - renderer.context.globalCompositeOperation = compositeOperation; - } - - // Ignore null sources - if (this.texture.valid) - { - var texture = this._texture, - wt = this.worldTransform, - dx, - dy, - width = texture.crop.width, - height = texture.crop.height; - - renderer.context.globalAlpha = this.worldAlpha; - - // If smoothingEnabled is supported and we need to change the smoothing property for this texture - var smoothingEnabled = texture.baseTexture.scaleMode === CONST.SCALE_MODES.LINEAR; - if (renderer.smoothProperty && renderer.context[renderer.smoothProperty] !== smoothingEnabled) - { - renderer.context[renderer.smoothProperty] = smoothingEnabled; - } - - - //inline GroupD8.isSwapWidthHeight - if ((texture.rotate & 3) === 2) { - width = texture.crop.height; - height = texture.crop.width; - } - if (texture.trim) { - dx = texture.crop.width/2 + texture.trim.x - this.anchor.x * texture.trim.width; - dy = texture.crop.height/2 + texture.trim.y - this.anchor.y * texture.trim.height; - } else { - dx = (0.5 - this.anchor.x) * texture._frame.width; - dy = (0.5 - this.anchor.y) * texture._frame.height; - } - if(texture.rotate) { - wt.copy(canvasRenderWorldTransform); - wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); - // the anchor has already been applied above, so lets set it to zero - dx = 0; - dy = 0; - } - dx -= width/2; - dy -= height/2; - // Allow for pixel rounding - if (renderer.roundPixels) - { - renderer.context.setTransform( - wt.a, - wt.b, - wt.c, - wt.d, - (wt.tx * renderer.resolution) | 0, - (wt.ty * renderer.resolution) | 0 - ); - - dx = dx | 0; - dy = dy | 0; - } - else - { - renderer.context.setTransform( - wt.a, - wt.b, - wt.c, - wt.d, - wt.tx * renderer.resolution, - wt.ty * renderer.resolution - ); - } - - var resolution = texture.baseTexture.resolution; - - if (this.tint !== 0xFFFFFF) - { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - // TODO clean up caching - how to clean up the caches? - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } - - renderer.context.drawImage( - this.tintedTexture, - 0, - 0, - width * resolution, - height * resolution, - dx * renderer.resolution, - dy * renderer.resolution, - width * renderer.resolution, - height * renderer.resolution - ); - } - else - { - renderer.context.drawImage( - texture.baseTexture.source, - texture.frame.x * resolution, - texture.frame.y * resolution, - width * resolution, - height * resolution, - dx * renderer.resolution, - dy * renderer.resolution, - width * renderer.resolution, - height * renderer.resolution - ); - } - } -}; /** * Destroys this sprite and optionally its texture diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 59012a9..50f33bc 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -2,7 +2,6 @@ Texture = require('../textures/Texture'), RenderTexture = require('../textures/RenderTexture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), - CanvasGraphics = require('../renderers/canvas/utils/CanvasGraphics'), GraphicsData = require('./GraphicsData'), Sprite = require('../sprites/Sprite'), math = require('../math'), @@ -803,7 +802,7 @@ transform.ty * resolution ); - CanvasGraphics.renderGraphics(this, context); + renderer.plugins.graphics.render(this); }; /** diff --git a/src/core/graphics/canvas/CanvasGraphicsRenderer.js b/src/core/graphics/canvas/CanvasGraphicsRenderer.js new file mode 100644 index 0000000..024402d --- /dev/null +++ b/src/core/graphics/canvas/CanvasGraphicsRenderer.js @@ -0,0 +1,250 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasGraphicsRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasGraphicsRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasGraphicsRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasGraphicsRenderer.prototype.constructor = CanvasGraphicsRenderer; +module.exports = CanvasGraphicsRenderer; + +CanvasRenderer.registerPlugin('graphics', CanvasGraphicsRenderer); + +/* + * Renders a Graphics object to a canvas. + * + * @param graphics {PIXI.Graphics} the actual graphics object to render + * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas + */ +CanvasGraphicsRenderer.prototype.render = function (graphics) +{ + var renderer = this.renderer; + var context = renderer.context; + var worldAlpha = graphics.worldAlpha; + + if (graphics.dirty) + { + this.updateGraphicsTint(graphics); + graphics.dirty = false; + } + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + var shape = data.shape; + + var fillColor = data._fillTint; + var lineColor = data._lineTint; + + context.lineWidth = data.lineWidth; + + if (data.type === CONST.SHAPES.POLY) + { + context.beginPath(); + + var points = shape.points; + + context.moveTo(points[0], points[1]); + + for (var j=1; j < points.length/2; j++) + { + context.lineTo(points[j * 2], points[j * 2 + 1]); + } + + if (shape.closed) + { + context.lineTo(points[0], points[1]); + } + + // if the first and last point are the same close the path - much neater :) + if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) + { + context.closePath(); + } + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RECT) + { + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fillRect(shape.x, shape.y, shape.width, shape.height); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.strokeRect(shape.x, shape.y, shape.width, shape.height); + } + } + else if (data.type === CONST.SHAPES.CIRC) + { + // TODO - need to be Undefined! + context.beginPath(); + context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.ELIP) + { + // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + + var w = shape.width * 2; + var h = shape.height * 2; + + var x = shape.x - w/2; + var y = shape.y - h/2; + + context.beginPath(); + + var kappa = 0.5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + context.moveTo(x, ym); + context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + context.closePath(); + + if (data.fill) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + else if (data.type === CONST.SHAPES.RREC) + { + var rx = shape.x; + var ry = shape.y; + var width = shape.width; + var height = shape.height; + var radius = shape.radius; + + var maxRadius = Math.min(width, height) / 2 | 0; + radius = radius > maxRadius ? maxRadius : radius; + + context.beginPath(); + context.moveTo(rx, ry + radius); + context.lineTo(rx, ry + height - radius); + context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); + context.lineTo(rx + width - radius, ry + height); + context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); + context.lineTo(rx + width, ry + radius); + context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); + context.lineTo(rx + radius, ry); + context.quadraticCurveTo(rx, ry, rx, ry + radius); + context.closePath(); + + if (data.fillColor || data.fillColor === 0) + { + context.globalAlpha = data.fillAlpha * worldAlpha; + context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); + context.fill(); + + } + if (data.lineWidth) + { + context.globalAlpha = data.lineAlpha * worldAlpha; + context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); + context.stroke(); + } + } + } +}; + +/* + * Updates the tint of a graphics object + * + * @private + * @param graphics {PIXI.Graphics} the graphics that will have its tint updated + * + */ +CanvasGraphicsRenderer.prototype.updateGraphicsTint = function (graphics) +{ + if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) + { + return; + } + + graphics._prevTint = graphics.tint; + + var tintR = (graphics.tint >> 16 & 0xFF) / 255; + var tintG = (graphics.tint >> 8 & 0xFF) / 255; + var tintB = (graphics.tint & 0xFF)/ 255; + + for (var i = 0; i < graphics.graphicsData.length; i++) + { + var data = graphics.graphicsData[i]; + + var fillColor = data.fillColor | 0; + var lineColor = data.lineColor | 0; + + // super inline cos im an optimization NAZI :) + data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); + data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); + } +}; + diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 840560c..761b22c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -83,16 +83,7 @@ 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); @@ -109,47 +100,54 @@ * * @param object {PIXI.DisplayObject} the object to be rendered */ -CanvasRenderer.prototype.render = function (object) +CanvasRenderer.prototype.render = function (displayObject, renderTexture, clear, transform, skipUpdateTransform) { + var context = this.context; + this.emit('prerender'); - var cacheParent = object.parent; + this._lastObjectRendered = displayObject; - this._lastObjectRendered = object; + if(!skipUpdateTransform) + { + // update the scene graph + var cacheParent = displayObject.parent; + displayObject.parent = this._tempDisplayObjectParent; + displayObject.updateTransform(); + displayObject.parent = cacheParent; + // displayObject.hitArea = //TODO add a temp hit area + } - 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]; + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalAlpha = 1; + context.globalCompositeOperation = this.blendModes[CONST.BLEND_MODES.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { - this.context.fillStyle = 'black'; - this.context.clear(); + context.fillStyle = 'black'; + context.clear(); } - if (this.clearBeforeRender) + if( clear || this.clearBeforeRender) { if (this.transparent) { - this.context.clearRect(0, 0, this.width, this.height); + context.clearRect(0, 0, this.width, this.height); } else { - this.context.fillStyle = this._backgroundColorString; - this.context.fillRect(0, 0, this.width , this.height); + context.fillStyle = this._backgroundColorString; + context.fillRect(0, 0, this.width , this.height); } } - this.renderDisplayObject(object, this.context); + // TODO RENDER TARGET STUFF HERE.. + var tempContext = this.context; + + this.context = context; + displayObject.renderCanvas(this); + this.context = tempContext; this.emit('postrender'); }; @@ -177,21 +175,6 @@ }; /** - * 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 diff --git a/src/core/renderers/canvas/utils/CanvasGraphics.js b/src/core/renderers/canvas/utils/CanvasGraphics.js deleted file mode 100644 index b2d5b91..0000000 --- a/src/core/renderers/canvas/utils/CanvasGraphics.js +++ /dev/null @@ -1,352 +0,0 @@ -var CONST = require('../../../const'); - -/** - * A set of functions used by the canvas renderer to draw the primitive graphics data. - * @static - * @class - * @memberof PIXI - */ -var CanvasGraphics = {}; -module.exports = CanvasGraphics; - -/* - * Renders a Graphics object to a canvas. - * - * @param graphics {PIXI.Graphics} the actual graphics object to render - * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas - */ -CanvasGraphics.renderGraphics = function (graphics, context) -{ - var worldAlpha = graphics.worldAlpha; - - if (graphics.dirty) - { - this.updateGraphicsTint(graphics); - graphics.dirty = false; - } - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - var fillColor = data._fillTint; - var lineColor = data._lineTint; - - context.lineWidth = data.lineWidth; - - if (data.type === CONST.SHAPES.POLY) - { - context.beginPath(); - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - if (shape.closed) - { - context.lineTo(points[0], points[1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RECT) - { - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fillRect(shape.x, shape.y, shape.width, shape.height); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.strokeRect(shape.x, shape.y, shape.width, shape.height); - } - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.beginPath(); - context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.ELIP) - { - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - context.beginPath(); - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - - context.closePath(); - - if (data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === CONST.SHAPES.RREC) - { - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - - if (data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - - } - if (data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - } -}; - -/* - * Renders a graphics mask - * - * @private - * @param graphics {PIXI.Graphics} the graphics which will be used as a mask - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas - */ -CanvasGraphics.renderGraphicsMask = function (graphics, context) -{ - var len = graphics.graphicsData.length; - - if (len === 0) - { - return; - } - - context.beginPath(); - - for (var i = 0; i < len; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - if (data.type === CONST.SHAPES.POLY) - { - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - // if the first and last point are the same close the path - much neater :) - if (points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - } - else if (data.type === CONST.SHAPES.RECT) - { - context.rect(shape.x, shape.y, shape.width, shape.height); - context.closePath(); - } - else if (data.type === CONST.SHAPES.CIRC) - { - // TODO - need to be Undefined! - context.arc(shape.x, shape.y, shape.radius, 0, 2 * Math.PI); - context.closePath(); - } - else if (data.type === CONST.SHAPES.ELIP) - { - - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - } - else if (data.type === CONST.SHAPES.RREC) - { - - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - } - } -}; - -/* - * Updates the tint of a graphics object - * - * @private - * @param graphics {PIXI.Graphics} the graphics that will have its tint updated - * - */ -CanvasGraphics.updateGraphicsTint = function (graphics) -{ - if (graphics.tint === 0xFFFFFF && graphics._prevTint === graphics.tint) - { - return; - } - graphics._prevTint = graphics.tint; - - var tintR = (graphics.tint >> 16 & 0xFF) / 255; - var tintG = (graphics.tint >> 8 & 0xFF) / 255; - var tintB = (graphics.tint & 0xFF)/ 255; - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - - var fillColor = data.fillColor | 0; - var lineColor = data.lineColor | 0; - - /* - var colorR = (fillColor >> 16 & 0xFF) / 255; - var colorG = (fillColor >> 8 & 0xFF) / 255; - var colorB = (fillColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - - colorR = (lineColor >> 16 & 0xFF) / 255; - colorG = (lineColor >> 8 & 0xFF) / 255; - colorB = (lineColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - */ - - // super inline cos im an optimization NAZI :) - data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); - data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); - - } -}; - diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 9e46aed..7e65e3e 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -270,6 +270,18 @@ }; /** +* Renders the object using the Canvas renderer +* +* @param renderer {PIXI.CanvasRenderer} The renderer +* @private +*/ +Sprite.prototype._renderCanvas = function (renderer) +{ + renderer.plugins.sprite.render(this); +}; + + +/** * Returns the bounds of the Sprite as a rectangle. The bounds calculation takes the worldTransform into account. * * @param matrix {PIXI.Matrix} the transformation matrix of the sprite @@ -400,134 +412,6 @@ return false; }; -/** -* Renders the object using the Canvas renderer -* -* @param renderer {PIXI.CanvasRenderer} The renderer -* @private -*/ -Sprite.prototype._renderCanvas = function (renderer) -{ - if (this.texture.crop.width <= 0 || this.texture.crop.height <= 0) - { - return; - } - - var compositeOperation = renderer.blendModes[this.blendMode]; - if (compositeOperation !== renderer.context.globalCompositeOperation) - { - renderer.context.globalCompositeOperation = compositeOperation; - } - - // Ignore null sources - if (this.texture.valid) - { - var texture = this._texture, - wt = this.worldTransform, - dx, - dy, - width = texture.crop.width, - height = texture.crop.height; - - renderer.context.globalAlpha = this.worldAlpha; - - // If smoothingEnabled is supported and we need to change the smoothing property for this texture - var smoothingEnabled = texture.baseTexture.scaleMode === CONST.SCALE_MODES.LINEAR; - if (renderer.smoothProperty && renderer.context[renderer.smoothProperty] !== smoothingEnabled) - { - renderer.context[renderer.smoothProperty] = smoothingEnabled; - } - - - //inline GroupD8.isSwapWidthHeight - if ((texture.rotate & 3) === 2) { - width = texture.crop.height; - height = texture.crop.width; - } - if (texture.trim) { - dx = texture.crop.width/2 + texture.trim.x - this.anchor.x * texture.trim.width; - dy = texture.crop.height/2 + texture.trim.y - this.anchor.y * texture.trim.height; - } else { - dx = (0.5 - this.anchor.x) * texture._frame.width; - dy = (0.5 - this.anchor.y) * texture._frame.height; - } - if(texture.rotate) { - wt.copy(canvasRenderWorldTransform); - wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); - // the anchor has already been applied above, so lets set it to zero - dx = 0; - dy = 0; - } - dx -= width/2; - dy -= height/2; - // Allow for pixel rounding - if (renderer.roundPixels) - { - renderer.context.setTransform( - wt.a, - wt.b, - wt.c, - wt.d, - (wt.tx * renderer.resolution) | 0, - (wt.ty * renderer.resolution) | 0 - ); - - dx = dx | 0; - dy = dy | 0; - } - else - { - renderer.context.setTransform( - wt.a, - wt.b, - wt.c, - wt.d, - wt.tx * renderer.resolution, - wt.ty * renderer.resolution - ); - } - - var resolution = texture.baseTexture.resolution; - - if (this.tint !== 0xFFFFFF) - { - if (this.cachedTint !== this.tint) - { - this.cachedTint = this.tint; - - // TODO clean up caching - how to clean up the caches? - this.tintedTexture = CanvasTinter.getTintedTexture(this, this.tint); - } - - renderer.context.drawImage( - this.tintedTexture, - 0, - 0, - width * resolution, - height * resolution, - dx * renderer.resolution, - dy * renderer.resolution, - width * renderer.resolution, - height * renderer.resolution - ); - } - else - { - renderer.context.drawImage( - texture.baseTexture.source, - texture.frame.x * resolution, - texture.frame.y * resolution, - width * resolution, - height * resolution, - dx * renderer.resolution, - dy * renderer.resolution, - width * renderer.resolution, - height * renderer.resolution - ); - } - } -}; /** * Destroys this sprite and optionally its texture diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js new file mode 100644 index 0000000..08854d8 --- /dev/null +++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js @@ -0,0 +1,162 @@ +var CanvasRenderer = require('../../renderers/canvas/CanvasRenderer'), + CONST = require('../../const') + +/** + * @author Mat Groves + * + * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ + * for creating the original pixi version! + * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer + * + * Heavily inspired by LibGDX's CanvasSpriteRenderer: + * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/CanvasSpriteRenderer.java + */ + +/** + * Renderer dedicated to drawing and batching sprites. + * + * @class + * @private + * @memberof PIXI + * @extends PIXI.ObjectRenderer + * @param renderer {PIXI.WebGLRenderer} The renderer sprite this batch works for. + */ +function CanvasSpriteRenderer(renderer) +{ + this.renderer = renderer; +} + + +CanvasSpriteRenderer.prototype.constructor = CanvasSpriteRenderer; +module.exports = CanvasSpriteRenderer; + +CanvasRenderer.registerPlugin('sprite', CanvasSpriteRenderer); + +/** + * Renders the sprite object. + * + * @param sprite {PIXI.Sprite} the sprite to render when using this spritebatch + */ +CanvasSpriteRenderer.prototype.render = function (sprite) +{ + var texture = sprite._texture, + renderer = this.renderer, + wt = sprite.transform.worldTransform, + dx, + dy, + width = texture.crop.width, + height = texture.crop.height; + + if (texture.crop.width <= 0 || texture.crop.height <= 0) + { + return; + } + + var compositeOperation = renderer.blendModes[this.blendMode]; + if (compositeOperation !== renderer.context.globalCompositeOperation) + { + renderer.context.globalCompositeOperation = compositeOperation; + } + + // Ignore null sources + if (texture.valid) + { + renderer.context.globalAlpha = sprite.worldAlpha; + + // If smoothingEnabled is supported and we need to change the smoothing property for sprite texture + var smoothingEnabled = texture.baseTexture.scaleMode === CONST.SCALE_MODES.LINEAR; + if (renderer.smoothProperty && renderer.context[renderer.smoothProperty] !== smoothingEnabled) + { + renderer.context[renderer.smoothProperty] = smoothingEnabled; + } + + + //inline GroupD8.isSwapWidthHeight + if ((texture.rotate & 3) === 2) { + width = texture.crop.height; + height = texture.crop.width; + } + if (texture.trim) { + dx = texture.crop.width/2 + texture.trim.x - sprite.anchor.x * texture.trim.width; + dy = texture.crop.height/2 + texture.trim.y - sprite.anchor.y * texture.trim.height; + } else { + dx = (0.5 - sprite.anchor.x) * texture._frame.width; + dy = (0.5 - sprite.anchor.y) * texture._frame.height; + } + if(texture.rotate) { + wt.copy(canvasRenderWorldTransform); + wt = canvasRenderWorldTransform; + GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + // the anchor has already been applied above, so lets set it to zero + dx = 0; + dy = 0; + } + dx -= width/2; + dy -= height/2; + // Allow for pixel rounding + if (renderer.roundPixels) + { + renderer.context.setTransform( + wt.a, + wt.b, + wt.c, + wt.d, + (wt.tx * renderer.resolution) | 0, + (wt.ty * renderer.resolution) | 0 + ); + + dx = dx | 0; + dy = dy | 0; + } + else + { + renderer.context.setTransform( + wt.a, + wt.b, + wt.c, + wt.d, + wt.tx * renderer.resolution, + wt.ty * renderer.resolution + ); + } + + var resolution = texture.baseTexture.resolution; + + if (sprite.tint !== 0xFFFFFF) + { + if (sprite.cachedTint !== sprite.tint) + { + sprite.cachedTint = sprite.tint; + + // TODO clean up caching - how to clean up the caches? + sprite.tintedTexture = CanvasTinter.getTintedTexture(sprite, sprite.tint); + } + + renderer.context.drawImage( + sprite.tintedTexture, + 0, + 0, + width * resolution, + height * resolution, + dx * renderer.resolution, + dy * renderer.resolution, + width * renderer.resolution, + height * renderer.resolution + ); + } + else + { + renderer.context.drawImage( + texture.baseTexture.source, + texture.frame.x * resolution, + texture.frame.y * resolution, + width * resolution, + height * resolution, + dx * renderer.resolution, + dy * renderer.resolution, + width * renderer.resolution, + height * renderer.resolution + ); + } + } +};