diff --git a/src/easeljs/display/Bitmap.js b/src/easeljs/display/Bitmap.js
index db90d5fec..497828ce5 100644
--- a/src/easeljs/display/Bitmap.js
+++ b/src/easeljs/display/Bitmap.js
@@ -212,7 +212,10 @@ this.createjs = this.createjs||{};
**/
p.clone = function(node) {
var image = this.image;
- if(image && node){ image = image.cloneNode(); }
+ if(image && node){
+ image = image.cloneNode();
+ image.src = image.src; // IE cloneNode bug fix
+ }
var o = new Bitmap(image);
if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
this._cloneProps(o);
diff --git a/src/easeljs/display/Container.js b/src/easeljs/display/Container.js
index 4810cfb21..092df39e3 100644
--- a/src/easeljs/display/Container.js
+++ b/src/easeljs/display/Container.js
@@ -705,4 +705,4 @@ this.createjs = this.createjs||{};
createjs.Container = createjs.promote(Container, "DisplayObject");
-}());
\ No newline at end of file
+}());
diff --git a/src/easeljs/display/DisplayObject.js b/src/easeljs/display/DisplayObject.js
index 190bbd6e2..9216d7783 100644
--- a/src/easeljs/display/DisplayObject.js
+++ b/src/easeljs/display/DisplayObject.js
@@ -153,7 +153,7 @@ this.createjs = this.createjs||{};
* If a cache is active, this returns the canvas that holds the image of this display object. See {{#crossLink "DisplayObject/cache:method"}}{{/crossLink}}
* for more information. Use this to display the result of a cache. This will be a HTMLCanvasElement unless special cache rules have been deliberately enabled for this cache.
* @property cacheCanvas
- * @type {HTMLCanvasElement | Object}
+ * @type {HTMLCanvasElement | WebGLTexture | Object}
* @default null
* @readonly
**/
@@ -457,6 +457,15 @@ this.createjs = this.createjs||{};
* @default 0
*/
this._webGLRenderStyle = DisplayObject._StageGL_NONE;
+
+ /**
+ * Storage for the calculated position of an object in StageGL. If not using StageGL, you can null it to save memory.
+ * @property _glMtx
+ * @protected
+ * @type {Matrix2D}
+ * @default null
+ */
+ this._glMtx = new createjs.Matrix2D();
}
var p = createjs.extend(DisplayObject, createjs.EventDispatcher);
@@ -751,7 +760,7 @@ this.createjs = this.createjs||{};
**/
p.draw = function(ctx, ignoreCache) {
var cache = this.bitmapCache;
- if(cache && !ignoreCache) {
+ if (cache && !ignoreCache) {
return cache.draw(ctx);
}
return false;
@@ -826,8 +835,10 @@ this.createjs = this.createjs||{};
* @param {Object} [options=undefined] Specify additional parameters for the cache logic
**/
p.cache = function(x, y, width, height, scale, options) {
- if(!this.bitmapCache){
+ if (!this.bitmapCache){
this.bitmapCache = new createjs.BitmapCache();
+ } else {
+ this.bitmapCache._autoGenerated = false;
}
this.bitmapCache.define(this, x, y, width, height, scale, options);
};
@@ -855,7 +866,7 @@ this.createjs = this.createjs||{};
* whatwg spec on compositing.
**/
p.updateCache = function(compositeOperation) {
- if(!this.bitmapCache) {
+ if (!this.bitmapCache) {
throw "cache() must be called before updateCache()";
}
this.bitmapCache.update(compositeOperation);
@@ -866,7 +877,7 @@ this.createjs = this.createjs||{};
* @method uncache
**/
p.uncache = function() {
- if(this.bitmapCache) {
+ if (this.bitmapCache) {
this.bitmapCache.release();
this.bitmapCache = undefined;
}
@@ -996,8 +1007,9 @@ this.createjs = this.createjs||{};
* @return {Matrix2D} A matrix representing this display object's transform.
**/
p.getMatrix = function(matrix) {
- var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D();
- return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
+ var o = this, mtx = matrix || new createjs.Matrix2D();
+ return o.transformMatrix ? mtx.copy(o.transformMatrix) :
+ (mtx.identity() && mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY));
};
/**
@@ -1254,6 +1266,7 @@ this.createjs = this.createjs||{};
o.hitArea = this.hitArea;
o.cursor = this.cursor;
o._bounds = this._bounds;
+ o._webGLRenderStyle = this._webGLRenderStyle;
return o;
};
diff --git a/src/easeljs/display/Stage.js b/src/easeljs/display/Stage.js
index 380c1fbea..a6c675ccb 100644
--- a/src/easeljs/display/Stage.js
+++ b/src/easeljs/display/Stage.js
@@ -376,7 +376,24 @@ this.createjs = this.createjs||{};
ctx.restore();
this.dispatchEvent("drawend");
};
-
+
+ /**
+ * Draws the stage into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ var result = this.Container_draw(ctx, ignoreCache);
+ this.canvas._invalid = true;
+ return result;
+ };
+
/**
* Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
diff --git a/src/easeljs/display/StageGL.js b/src/easeljs/display/StageGL.js
index 414a5c35a..5522ee793 100644
--- a/src/easeljs/display/StageGL.js
+++ b/src/easeljs/display/StageGL.js
@@ -34,29 +34,39 @@ this.createjs = this.createjs||{};
/*
* README IF EDITING:
- * Terminology for developers:
*
+ * - Terminology for developers:
* Vertex: a point that help defines a shape, 3 per triangle. Usually has an x,y,z but can have more/less info.
* Vertex Property: a piece of information attached to the vertex like a vector3 containing x,y,z
* Index/Indices: used in groups of 3 to define a triangle, points to vertices by their index in an array (some render
* modes do not use these)
* Card: a group of 2 triangles used to display a rectangular image
- * U/V: common names for the [0-1] texture co-ordinates on an image
- * Batch: a single call to the renderer, best done as little as possible so multiple cards are put into a single batch
- * Buffer: WebGL array data
+ * UV: common names for the [0-1] texture co-ordinates on an image
+ * Batch: a single call to the renderer, best done as little as possible. Multiple cards are batched for this reason
* Program/Shader: For every vertex we run the Vertex shader. The results are used per pixel by the Fragment shader. When
- * combined and paired these are a shader "program"
- * Texture: WebGL representation of image data and associated extra information
- * Slot: A space on the GPU into which textures can be loaded for use in a batch, using "ActiveTexture" switches texture slot.
+ * combined and paired these are a "shader program"
+ * Texture: WebGL representation of image data and associated extra information, separate from a DOM Image
+ * Slot: A space on the GPU into which textures can be loaded for use in a batch, i.e. using "ActiveTexture" switches texture slot.
+ * Render___: actual WebGL draw call
+ * Buffer: WebGL array data
+ * Cover: A card that covers the entire viewport
+ *
+ * - Notes:
+ * WebGL treats 0,0 as the bottom left, as such there's a lot of co-ordinate space flipping to make regular canvas
+ * numbers make sense to users and WebGL simultaneously. This extends to textures stored in memory too. If writing
+ * code that deals with x/y, be aware your y may be flipped.
+ * Older versions had distinct internal paths for filters and regular draws, these have been merged.
+ * Draws are slowly assembled out of found content. Overflowing things like shaders, object/texture count will cause
+ * an early draw before continuing. Lookout for the things that force a draw. Marked with <------------------------
*/
(function () {
"use strict";
/**
- * A StageGL instance is the root level {{#crossLink "Container"}}{{/crossLink}} for an WebGL-optimized display list,
- * which is used in place of the usual {{#crossLink "Stage"}}{{/crossLink}}. This class should behave identically to
- * a {{#crossLink "Stage"}}{{/crossLink}} except for WebGL-specific functionality.
+ * A StageGL instance is the root level {{#crossLink "Container"}}{{/crossLink}} for a WebGL-optimized display list,
+ * which can be used in place of the usual {{#crossLink "Stage"}}{{/crossLink}}. This class should behave identically
+ * to a {{#crossLink "Stage"}}{{/crossLink}} except for WebGL-specific functionality.
*
* Each time the {{#crossLink "Stage/tick"}}{{/crossLink}} method is called, the display list is rendered to the
* target <canvas/> instance, ignoring non-WebGL-compatible display objects. On devices and browsers that don't
@@ -77,6 +87,7 @@ this.createjs = this.createjs||{};
* resize your canvas after making a StageGL instance, this will properly size the WebGL context stored in memory.
* - Best performance in demanding scenarios will come from manual management of texture memory, but it is handled
* automatically by default. See {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} for details.
+ * - Disable `directDraw` to get access to cacheless filters and composite oeprations!
*
*
Example
* This example creates a StageGL instance, adds a child to it, then uses the EaselJS {{#crossLink "Ticker"}}{{/crossLink}}
@@ -94,41 +105,41 @@ this.createjs = this.createjs||{};
* stage.update();
* }
*
- * Notes
- * - StageGL is not currently included in the minified version of EaselJS.
- * - {{#crossLink "SpriteContainer"}}{{/crossLink}} (the previous approach to WebGL with EaselJS) has been deprecated.
- * - Earlier versions of WebGL support in EaselJS (SpriteStage and SpriteContainer) had hard limitations on images
- * per container, which have been solved.
- *
* @class StageGL
* @extends Stage
* @constructor
* @param {HTMLCanvasElement | String | Object} canvas A canvas object that StageGL will render to, or the string id
- * of a canvas object in the current DOM.
+ * of a canvas object in the current DOM.
* @param {Object} options All the option parameters in a reference object, some are not supported by some browsers.
* @param {Boolean} [options.preserveBuffer=false] If `true`, the canvas is NOT auto-cleared by WebGL (the spec
- * discourages setting this to `true`). This is useful if you want persistent draw effects.
+ * discourages setting this to `true`). This is useful if you want persistent draw effects and has also fixed device
+ * specific bugs due to mis-timed clear commands.
* @param {Boolean} [options.antialias=false] Specifies whether or not the browser's WebGL implementation should try
- * to perform anti-aliasing. This will also enable linear pixel sampling on power-of-two textures (smoother images).
+ * to perform anti-aliasing. This will also enable linear pixel sampling on power-of-two textures (smoother images).
* @param {Boolean} [options.transparent=false] If `true`, the canvas is transparent. This is very
* expensive, and should be used with caution.
- * @param {Boolean} [options.premultiply=false] Alters color handling. If `true`, this assumes the shader must
- * account for pre-multiplied alpha. This can help avoid visual halo effects with some assets, but may also cause
- * problems with other assets.
- * @param {Integer} [options.autoPurge=1200] How often the system should automatically dump unused textures with
- * `purgeTextures(autoPurge)` every `autoPurge/2` draws. See {{#crossLink "StageGL/purgeTextures"}}{{/crossLink}} for more
- * information.
+ * @param {Boolean} [options.directDraw=true] If `true`, this will bypass intermediary render-textures when possible
+ * resulting in reduced memory and increased performance, this disables some features. Cache-less filters and some
+ * {{#crossLink "DisplayObject/compositeOperation:property"}}{{/crossLink}} values rely on this being false.
+ * @param (Boolean} [options.premultiply] @deprecated Upgraded colour & transparency handling have fixed the issue
+ * this flag was trying to solve rendering it unnecessary.
+ * @param {int} [options.autoPurge=1200] How often the system should automatically dump unused textures. Calls
+ * `purgeTextures(autoPurge)` every `autoPurge/2` draws. See {{#crossLink "StageGL/purgeTextures"}}{{/crossLink}}
+ * for more information on texture purging.
+ * @param {String|int} [options.clearColor=undefined] Automatically calls {{#crossLink "StageGL/setClearColor"}}{{/crossLink}}
+ * after init is complete, can be overridden and changed manually later.
*/
function StageGL(canvas, options) {
this.Stage_constructor(canvas);
+ var transparent, antialias, preserveBuffer, autoPurge, directDraw;
if (options !== undefined) {
if (typeof options !== "object"){ throw("Invalid options object"); }
- var premultiply = options.premultiply;
- var transparent = options.transparent;
- var antialias = options.antialias;
- var preserveBuffer = options.preserveBuffer;
- var autoPurge = options.autoPurge;
+ transparent = options.transparent;
+ antialias = options.antialias;
+ preserveBuffer = options.preserveBuffer;
+ autoPurge = options.autoPurge;
+ directDraw = options.directDraw;
}
// public properties:
@@ -141,6 +152,18 @@ this.createjs = this.createjs||{};
*/
this.vocalDebug = false;
+ /**
+ * Specifies whether this instance is slaved to a {{#crossLink "BitmapCache"}}{{/crossLink}} or draws independantly.
+ * Necessary to control texture outputs and behaviours when caching. StageGL cache outputs will only render
+ * properly for the StageGL that made them. See the {{#crossLink "cache"}}{{/crossLink}} function documentation
+ * for more information. Enabled by default when BitmapCache's `useGL` is true.
+ * NOTE: This property is mainly for internal use, though it may be useful for advanced uses.
+ * @property isCacheControlled
+ * @type {Boolean}
+ * @default false
+ */
+ this.isCacheControlled = false;
+
// private properties:
/**
* Specifies whether or not the canvas is auto-cleared by WebGL. The WebGL spec discourages `true`.
@@ -171,24 +194,24 @@ this.createjs = this.createjs||{};
*/
this._transparent = transparent||false;
- /**
- * Specifies whether or not StageGL is handling colours as premultiplied alpha.
- * @property _premultiply
- * @protected
- * @type {Boolean}
- * @default false
- */
- this._premultiply = premultiply||false;
-
/**
* Internal value of {{#crossLink "StageGL/autoPurge"}}{{/crossLink}}
* @property _autoPurge
* @protected
- * @type {Integer}
+ * @type {int}
* @default null
*/
this._autoPurge = undefined;
- this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating
+ this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating and documentation
+
+ /**
+ * See directDraw
+ * @property _directDraw
+ * @protected
+ * @type {Boolean}
+ * @default false
+ */
+ this._directDraw = directDraw === undefined ? true : false;
/**
* The width in px of the drawing surface saved in memory.
@@ -227,6 +250,13 @@ this.createjs = this.createjs||{};
*/
this._webGLContext = null;
+ /**
+ * Reduce API logic by allowing stage to behave as a renderTexture target, should always be null as null is canvas.
+ * @type {null}
+ * @protected
+ */
+ this._frameBuffer = null;
+
/**
* The color to use when the WebGL canvas has been cleared. May appear as a background color. Defaults to grey.
* @property _clearColor
@@ -239,12 +269,12 @@ this.createjs = this.createjs||{};
/**
* The maximum number of cards (aka a single sprite) that can be drawn in one draw call. Use getter/setters to
* modify otherwise internal buffers may be incorrect sizes.
- * @property _maxCardsPerBatch
+ * @property _maxBatchVertexCount
* @protected
* @type {Number}
- * @default StageGL.DEFAULT_MAX_BATCH_SIZE (10000)
+ * @default StageGL.DEFAULT_MAX_BATCH_SIZE * StageGL.INDICIES_PER_CARD
*/
- this._maxCardsPerBatch = StageGL.DEFAULT_MAX_BATCH_SIZE; //TODO: write getter/setters for this
+ this._maxBatchVertexCount = StageGL.DEFAULT_MAX_BATCH_SIZE * StageGL.INDICIES_PER_CARD;
/**
* The shader program used to draw the current batch.
@@ -255,6 +285,13 @@ this.createjs = this.createjs||{};
*/
this._activeShader = null;
+ /**
+ * The non cover, per object shader used for most rendering actions.
+ * @type {WebGLProgram}
+ * @protected
+ */
+ this._mainShader = null;
+
/**
* The vertex position data for the current draw call.
* @property _vertices
@@ -274,7 +311,7 @@ this.createjs = this.createjs||{};
this._vertexPositionBuffer = null;
/**
- * The vertex U/V data for the current draw call.
+ * The vertex UV data for the current draw call.
* @property _uvs
* @protected
* @type {Float32Array}
@@ -327,6 +364,67 @@ this.createjs = this.createjs||{};
*/
this._alphaBuffer = null;
+ /**
+ * One of the major render buffers used in composite blending drawing. Do not expect this to always be the same object.
+ * "What you're drawing to", object occasionally swaps with concat.
+ * @property _bufferTextureOutput
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this._bufferTextureOutput = null;
+
+ /**
+ * One of the major render buffers used in composite blending drawing. Do not expect this to always be the same object.
+ * "What you've draw before now", object occasionally swaps with output.
+ * @property _bufferTextureConcat
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this._bufferTextureConcat = null;
+
+ /**
+ * One of the major render buffers used in composite blending drawing.
+ * "Temporary mixing surface"
+ * @property _bufferTextureTemp
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this._bufferTextureTemp = null;
+
+ /**
+ * The current render buffer being targeted, usually targets internal buffers, but may be set to cache's buffer during a cache render.
+ * @property _batchTextureOutput
+ * @protected
+ * @type {WebGLTexture | StageGL}
+ */
+ this._batchTextureOutput = this;
+
+ /**
+ * The current render buffer being targeted, usually targets internal buffers, but may be set to cache's buffer during a cache render.
+ * @property _batchTextureConcat
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this._batchTextureConcat = null;
+
+ /**
+ * The current render buffer being targeted, usually targets internal buffers, but may be set to cache's buffer during a cache render.
+ * @property _batchTextureTemp
+ * @protected
+ * @type {WebGLTexture}
+ */
+ this._batchTextureTemp = null;
+
+ /**
+ * Internal library of the shaders that have been compiled and created along with their parameters. Should contain
+ * compiled `gl.ShaderProgram` and settings for `gl.blendFunc` and `gl.blendEquation`. Populated as requested.
+ *
+ * See {{#crossLink "StageGL/_updateRenderMode:method"}}{{/crossLink}} for exact details.
+ * @type {Object}
+ * @private
+ */
+ this._builtShaders = {};
+
/**
* An index based lookup of every WebGL Texture currently in use.
* @property _drawTexture
@@ -361,30 +459,59 @@ this.createjs = this.createjs||{};
this._baseTextures = [];
/**
- * The number of concurrent textures the GPU can handle. This value is dynamically set from WebGL during initialization
- * via `gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)`. The WebGL spec states that the lowest guaranteed value is 8,
- * but it could be higher. Do not set this value higher than the value returned by the GPU. Setting it lower will
- * probably reduce performance, but may be advisable to reserve slots for custom filter work.
- * NOTE: Can also act as a length for {{#crossLink "StageGL/_batchTextures:property"}}.
- * @property _batchTextureCount
+ * Texture slots for a draw
+ * @property _gpuTextureCount
* @protected
- * @type {Number}
- * @default 8
+ * @type {uint}
*/
- this._batchTextureCount = 8;
+ this._gpuTextureCount = 8;
/**
- * The location at which the last texture was inserted into a GPU slot in {{#crossLink "StageGL/_batchTextures:property"}}{{/crossLink}}.
- * Manual control of this variable can yield improvements in performance by intelligently replacing textures
- * inside a batch to reduce texture re-load. It is impossible to write automated general use code, as it requires
- * display list look ahead inspection and/or render foreknowledge.
- * @property _lastTextureInsert
+ * Texture slots on the hardware
+ * @property _gpuTextureMax
* @protected
- * @type {Number}
- * @default -1
+ * @type {uint}
+ */
+ this._gpuTextureMax = 8;
+
+ /**
+ * Texture slots in a batch for User textures
+ * @property _batchTextureCount
+ * @protected
+ * @type {uint}
+ */
+ this._batchTextureCount = 0;
+
+ /**
+ * The location at which the last texture was inserted into a GPU slot
*/
this._lastTextureInsert = -1;
+ /**
+ * The current string name of the render mode being employed per Context2D spec.
+ * Must start invalid to trigger default shader into being built during init.
+ * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
+ * @type {string}
+ * @private
+ */
+ this._renderMode = "";
+
+ /**
+ * Flag indicating that the content being batched in `appendToBatch` must be drawn now and not follow batch logic.
+ * Used for effects that are compounding in nature and cannot be applied in a single pass.
+ * Should be enabled with extreme care due to massive performance implications.
+ * @type {boolean}
+ * @private
+ */
+ this._immediateRender = false;
+
+ /**
+ * Vertices drawn into the batch so far.
+ * @type {number}
+ * @private
+ */
+ this._batchVertexCount = 0;
+
/**
* The current batch being drawn, A batch consists of a call to `drawElements` on the GPU. Many of these calls
* can occur per draw.
@@ -413,15 +540,6 @@ this.createjs = this.createjs||{};
*/
this._slotBlacklist = [];
- /**
- * Used to prevent nested draw calls from accidentally overwriting drawing information by tracking depth.
- * @property _isDrawing
- * @protected
- * @type {Number}
- * @default 0
- */
- this._isDrawing = 0;
-
/**
* Used to ensure every canvas used as a texture source has a unique ID.
* @property _lastTrackedCanvas
@@ -431,17 +549,6 @@ this.createjs = this.createjs||{};
*/
this._lastTrackedCanvas = -1;
- /**
- * Controls whether final rendering output of a {{#crossLink "cacheDraw"}}{{/crossLink}} is the canvas or a render
- * texture. See the {{#crossLink "cache"}}{{/crossLink}} function modifications for full implications and discussion.
- * @property isCacheControlled
- * @protected
- * @type {Boolean}
- * @default false
- * @todo LM: is this supposed to be _isCacheControlled since its private?
- */
- this.isCacheControlled = false;
-
/**
* Used to counter-position the object being cached so it aligns with the cache surface. Additionally ensures
* that all rendering starts with a top level container.
@@ -454,12 +561,18 @@ this.createjs = this.createjs||{};
// and begin
this._initializeWebGL();
+
+ // these settings require everything to be ready
+ if (options !== undefined) {
+ options.clearColor !== undefined && this.setClearColor(options.clearColor);
+ options.premultiply !== undefined && (createjs.deprecate(null, "options.premultiply")());
+ }
}
var p = createjs.extend(StageGL, createjs.Stage);
// static methods:
/**
- * Calculate the U/V co-ordinate based info for sprite frames. Instead of pixel count it uses a 0-1 space. Also includes
+ * Calculate the UV co-ordinate based info for sprite frames. Instead of pixel count it uses a 0-1 space. Also includes
* the ability to get info back for a specific frame, or only calculate that one frame.
*
* //generate UV rects for all entries
@@ -482,28 +595,28 @@ this.createjs = this.createjs||{};
if (target === undefined) { target = -1; }
if (onlyTarget === undefined) { onlyTarget = false; }
- var start = (target != -1 && onlyTarget)?(target):(0);
- var end = (target != -1 && onlyTarget)?(target+1):(spritesheet._frames.length);
+ var start = (target !== -1 && onlyTarget)?(target):(0);
+ var end = (target !== -1 && onlyTarget)?(target+1):(spritesheet._frames.length);
for (var i=start; i 0.0035) {" + // 1/255 = 0.0039, so ignore any value below 1 because it's probably noise
- "gl_FragColor = vec4(color.rgb/color.a, color.a * alphaValue);" +
- "} else {" +
- "gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);" +
- "}"
- );
-
- //TODO: DHG: a real particle shader
- /**
- * @property PARTICLE_VERTEX_BODY
- * @todo
- * @final
- * @static
- * @type {String}
- * @readonly
- */
- StageGL.PARTICLE_VERTEX_BODY = (
- StageGL.REGULAR_VERTEX_BODY
- );
- /**
- * @property PARTICLE_FRAGMENT_BODY
- * @todo
- * @final
- * @static
- * @type {String}
- * @readonly
- */
- StageGL.PARTICLE_FRAGMENT_BODY = (
- StageGL.REGULAR_FRAGMENT_BODY
- );
/**
* Portion of the shader that contains the "varying" properties required in both vertex and fragment shaders. The
* cover shader is designed to be a simple vertex/uv only texture render that covers the render surface. Shader
* code may contain templates that are replaced pre-compile.
* @property COVER_VARYING_HEADER
+ * @protected
* @static
* @final
* @type {String}
* @readonly
*/
StageGL.COVER_VARYING_HEADER = (
- "precision mediump float;" +
+ "precision highp float;" + //this is usually essential for filter math
- "varying highp vec2 vRenderCoord;" +
- "varying highp vec2 vTextureCoord;"
+ "varying vec2 vTextureCoord;"
);
/**
@@ -777,6 +840,7 @@ this.createjs = this.createjs||{};
* simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
* replaced pre-compile.
* @property COVER_VERTEX_HEADER
+ * @protected
* @static
* @final
* @type {String}
@@ -785,8 +849,7 @@ this.createjs = this.createjs||{};
StageGL.COVER_VERTEX_HEADER = (
StageGL.COVER_VARYING_HEADER +
"attribute vec2 vertexPosition;" +
- "attribute vec2 uvPosition;" +
- "uniform float uUpright;"
+ "attribute vec2 uvPosition;"
);
/**
@@ -794,6 +857,7 @@ this.createjs = this.createjs||{};
* simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
* replaced pre-compile.
* @property COVER_FRAGMENT_HEADER
+ * @protected
* @static
* @final
* @type {String}
@@ -808,6 +872,7 @@ this.createjs = this.createjs||{};
* Body of the vertex shader. The cover shader is designed to be a simple vertex/uv only texture render that covers
* the render surface. Shader code may contain templates that are replaced pre-compile.
* @property COVER_VERTEX_BODY
+ * @protected
* @static
* @final
* @type {String}
@@ -816,8 +881,7 @@ this.createjs = this.createjs||{};
StageGL.COVER_VERTEX_BODY = (
"void main(void) {" +
"gl_Position = vec4(vertexPosition.x, vertexPosition.y, 0.0, 1.0);" +
- "vRenderCoord = uvPosition;" +
- "vTextureCoord = vec2(uvPosition.x, abs(uUpright - uvPosition.y));" +
+ "vTextureCoord = uvPosition;" +
"}"
);
@@ -825,6 +889,7 @@ this.createjs = this.createjs||{};
* Body of the fragment shader. The cover shader is designed to be a simple vertex/uv only texture render that
* covers the render surface. Shader code may contain templates that are replaced pre-compile.
* @property COVER_FRAGMENT_BODY
+ * @protected
* @static
* @final
* @type {String}
@@ -832,11 +897,299 @@ this.createjs = this.createjs||{};
*/
StageGL.COVER_FRAGMENT_BODY = (
"void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
- "gl_FragColor = color;" +
+ "gl_FragColor = texture2D(uSampler, vTextureCoord);" +
"}"
);
+ /**
+ * The starting template of a cover fragment shader with simple blending equations
+ * @property BLEND_FRAGMENT_SIMPLE
+ * @protected
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.BLEND_FRAGMENT_SIMPLE = (
+ "uniform sampler2D uMixSampler;"+
+ "void main(void) {" +
+ "vec4 src = texture2D(uMixSampler, vTextureCoord);" +
+ "vec4 dst = texture2D(uSampler, vTextureCoord);"
+ // note this is an open bracket on main!
+ );
+
+ /**
+ * The starting template of a cover fragment shader which has complex blending equations
+ * @property BLEND_FRAGMENT_COMPLEX
+ * @protected
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.BLEND_FRAGMENT_COMPLEX = (
+ StageGL.BLEND_FRAGMENT_SIMPLE +
+ "vec3 srcClr = min(src.rgb/src.a, 1.0);" +
+ "vec3 dstClr = min(dst.rgb/dst.a, 1.0);" +
+
+ "float totalAlpha = min(1.0 - (1.0-dst.a) * (1.0-src.a), 1.0);" +
+ "float srcFactor = min(max(src.a - dst.a, 0.0) / totalAlpha, 1.0);" +
+ "float dstFactor = min(max(dst.a - src.a, 0.0) / totalAlpha, 1.0);" +
+ "float mixFactor = max(max(1.0 - srcFactor, 0.0) - dstFactor, 0.0);" +
+
+ "gl_FragColor = vec4(" +
+ "(" +
+ "srcFactor * srcClr +" +
+ "dstFactor * dstClr +" +
+ "mixFactor * vec3("
+ // this should be closed with the cap!
+ );
+
+ /**
+ * The closing portion of a template for a cover fragment shader which has complex blending equations
+ * @property BLEND_FRAGMENT_COMPLEX_CAP
+ * @protected
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.BLEND_FRAGMENT_COMPLEX_CAP = (
+ ")" +
+ ") * totalAlpha, totalAlpha" +
+ ");" +
+ "}"
+ );
+
+ /**
+ * A shader utility function, used to calculate the "overlay" blend of two elements
+ * @property BLEND_FRAGMENT_OVERLAY_UTIL
+ * @protected
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.BLEND_FRAGMENT_OVERLAY_UTIL = (
+ "float overlay(float a, float b) {" +
+ "if(a < 0.5) { return 2.0 * a * b; }" +
+ "return 1.0 - 2.0 * (1.0-a) * (1.0-b);" +
+ "}"
+ );
+
+ /**
+ * A collection of shader utility functions, used to calculate HSL math. Taken from W3C spec
+ * https://www.w3.org/TR/compositing-1/#blendingnonseparable
+ * @property BLEND_FRAGMENT_HSL_UTIL
+ * @protected
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.BLEND_FRAGMENT_HSL_UTIL = (
+ "float getLum(vec3 c) { return 0.299*c.r + 0.589*c.g + 0.109*c.b; }" +
+ "float getSat(vec3 c) { return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b); }" +
+ "vec3 clipHSL(vec3 c) {" +
+ "float lum = getLum(c);" +
+ "float n = min(min(c.r, c.g), c.b);" +
+ "float x = max(max(c.r, c.g), c.b);" +
+ "if(n < 0.0){ c = lum + (((c - lum) * lum) / (lum - n)); }" +
+ "if(x > 1.0){ c = lum + (((c - lum) * (1.0 - lum)) / (x - lum)); }" +
+ "return clamp(c, 0.0, 1.0);" +
+ "}" +
+ "vec3 setLum(vec3 c, float lum) {" +
+ "return clipHSL(c + (lum - getLum(c)));" +
+ "}" +
+ "vec3 setSat(vec3 c, float val) {" +
+ "vec3 result = vec3(0.0);" +
+ "float minVal = min(min(c.r, c.g), c.b);" +
+ "float maxVal = max(max(c.r, c.g), c.b);" +
+ "vec3 minMask = vec3(c.r == minVal, c.g == minVal, c.b == minVal);" +
+ "vec3 maxMask = vec3(c.r == maxVal, c.g == maxVal, c.b == maxVal);" +
+ "vec3 midMask = 1.0 - min(minMask+maxMask, 1.0);" +
+ "float midVal = (c*midMask).r + (c*midMask).g + (c*midMask).b;" +
+ "if(maxVal > minVal) {" +
+ "result = midMask * min( ((midVal - minVal) * val) / (maxVal - minVal), 1.0);" +
+ "result += maxMask * val;" +
+ "}" +
+ "return result;" +
+ "}"
+ );
+
+ /**
+ * The hash of supported blend modes and their properties
+ * @property BLEND_SOURCES
+ * @static
+ * @final
+ * @type {Object}
+ * @readonly
+ */
+ StageGL.BLEND_SOURCES = {
+ "source-over": { // empty object verifies it as a blend mode, but default values handle actual settings
+ //eqRGB: "FUNC_ADD", eqA: "FUNC_ADD"
+ //srcRGB: "ONE", srcA: "ONE"
+ //dstRGB: "ONE_MINUS_SRC_ALPHA", dstA: "ONE_MINUS_SRC_ALPHA"
+ },
+ "source-in": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "gl_FragColor = vec4(src.rgb * dst.a, src.a * dst.a);" +
+ "}")
+ },
+ "source-in_cheap": {
+ srcRGB: "DST_ALPHA", srcA: "ZERO",
+ dstRGB: "ZERO", dstA: "SRC_ALPHA"
+ },
+ "source-out": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "gl_FragColor = vec4(src.rgb * (1.0 - dst.a), src.a - dst.a);" +
+ "}")
+ },
+ "source-out_cheap": {
+ eqA: "FUNC_SUBTRACT",
+ srcRGB: "ONE_MINUS_DST_ALPHA", srcA: "ONE",
+ dstRGB: "ZERO", dstA: "SRC_ALPHA"
+ },
+ "source-atop": {
+ srcRGB: "DST_ALPHA", srcA: "ZERO",
+ dstRGB: "ONE_MINUS_SRC_ALPHA", dstA: "ONE"
+ },
+ "destination-over": {
+ srcRGB: "ONE_MINUS_DST_ALPHA", srcA: "ONE_MINUS_DST_ALPHA",
+ dstRGB: "ONE", dstA: "ONE"
+ },
+ "destination-in": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "gl_FragColor = vec4(dst.rgb * src.a, src.a * dst.a);" +
+ "}")
+ },
+ "destination-in_cheap": {
+ srcRGB: "ZERO", srcA: "DST_ALPHA",
+ dstRGB: "SRC_ALPHA", dstA: "ZERO"
+ },
+ "destination-out": {
+ eqA: "FUNC_REVERSE_SUBTRACT",
+ srcRGB: "ZERO", srcA: "DST_ALPHA",
+ dstRGB: "ONE_MINUS_SRC_ALPHA", dstA: "ONE"
+ },
+ "destination-atop": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "gl_FragColor = vec4(dst.rgb * src.a + src.rgb * (1.0 - dst.a), src.a);" +
+ "}")
+ },
+ "destination-atop_cheap": {
+ srcRGB: "ONE_MINUS_DST_ALPHA", srcA: "ONE",
+ dstRGB: "SRC_ALPHA", dstA: "ZERO"
+ },
+ "copy": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "gl_FragColor = vec4(src.rgb, src.a);" +
+ "}")
+ },
+ "copy_cheap": {
+ dstRGB: "ZERO", dstA: "ZERO"
+ },
+ "xor": {
+ shader: (StageGL.BLEND_FRAGMENT_SIMPLE +
+ "float omSRC = (1.0 - src.a);" +
+ "float omDST = (1.0 - dst.a);" +
+ "gl_FragColor = vec4(src.rgb * omDST + dst.rgb * omSRC, src.a * omDST + dst.a * omSRC);"
+ + "}")
+ },
+
+ "multiply": { // this has to be complex to handle retention of both dst and src in non mixed scenarios
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "srcClr * dstClr"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "multiply_cheap": { // NEW, handles retention of src data incorrectly when no dst data present
+ srcRGB: "ONE_MINUS_DST_ALPHA", srcA: "ONE",
+ dstRGB: "SRC_COLOR", dstA: "ONE"
+ },
+ "screen": {
+ srcRGB: "ONE", srcA: "ONE",
+ dstRGB: "ONE_MINUS_SRC_COLOR", dstA: "ONE_MINUS_SRC_ALPHA"
+ },
+ "lighter": {
+ dstRGB: "ONE", dstA:"ONE"
+ },
+ "lighten": { //WebGL 2.0 can optimize this
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "max(srcClr, dstClr)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "darken": { //WebGL 2.0 can optimize this
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "min(srcClr, dstClr)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+
+ "overlay": {
+ shader: (StageGL.BLEND_FRAGMENT_OVERLAY_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "overlay(dstClr.r,srcClr.r), overlay(dstClr.g,srcClr.g), overlay(dstClr.b,srcClr.b)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "hard-light": {
+ shader: (StageGL.BLEND_FRAGMENT_OVERLAY_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "overlay(srcClr.r,dstClr.r), overlay(srcClr.g,dstClr.g), overlay(srcClr.b,dstClr.b)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "soft-light": {
+ shader: (
+ "float softcurve(float a) {" +
+ "if(a > 0.25) { return sqrt(a); }" +
+ "return ((16.0 * a - 12.0) * a + 4.0) * a;" +
+ "}" +
+ "float softmix(float a, float b) {" +
+ "if(b <= 0.5) { return a - (1.0 - 2.0*b) * a * (1.0 - a); }" +
+ "return a + (2.0 * b - 1.0) * (softcurve(a) - a);" +
+ "}" + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "softmix(dstClr.r,srcClr.r), softmix(dstClr.g,srcClr.g), softmix(dstClr.b,srcClr.b)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "color-dodge": {
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "clamp(dstClr / (1.0 - srcClr), 0.0, 1.0)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "color-burn": {
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "1.0 - clamp((1.0 - smoothstep(0.0035, 0.9955, dstClr)) / smoothstep(0.0035, 0.9955, srcClr), 0.0, 1.0)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "difference": { // do this to match visible results in browsers
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "abs(src.rgb - dstClr)"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "exclusion": { // do this to match visible results in browsers
+ shader: (StageGL.BLEND_FRAGMENT_COMPLEX +
+ "dstClr + src.rgb - 2.0 * src.rgb * dstClr"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+
+ "hue": {
+ shader: (StageGL.BLEND_FRAGMENT_HSL_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "setLum(setSat(srcClr, getSat(dstClr)), getLum(dstClr))"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "saturation": {
+ shader: (StageGL.BLEND_FRAGMENT_HSL_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "setLum(setSat(dstClr, getSat(srcClr)), getLum(dstClr))"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "color": {
+ shader: (StageGL.BLEND_FRAGMENT_HSL_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "setLum(srcClr, getLum(dstClr))"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ },
+ "luminosity": {
+ shader: (StageGL.BLEND_FRAGMENT_HSL_UTIL + StageGL.BLEND_FRAGMENT_COMPLEX +
+ "setLum(dstClr, getLum(srcClr))"
+ + StageGL.BLEND_FRAGMENT_COMPLEX_CAP)
+ }
+ };
+
// events:
/**
* Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. You can call
@@ -856,7 +1209,7 @@ this.createjs = this.createjs||{};
p._set_autoPurge = function (value) {
value = isNaN(value)?1200:value;
- if (value != -1) {
+ if (value !== -1) {
value = value<10?10:value;
}
this._autoPurge = value;
@@ -878,10 +1231,12 @@ this.createjs = this.createjs||{};
/**
* Specifies whether or not StageGL is automatically purging unused textures. Higher numbers purge less
- * often. Values below 10 are upgraded to 10, and -1 disables this feature.
+ * often. Values below 10 are upgraded to 10, and -1 disables this feature. If you are not dynamically adding
+ * and removing new images this can be se9000t to -1, and should be for performance reasons. If you only add images,
+ * or add and remove the same images for the entire application, it is safe to turn off this feature. Alternately,
+ * manually manage removing textures yourself with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}}
* @property autoPurge
- * @protected
- * @type {Integer}
+ * @type {int}
* @default 1000
*/
autoPurge: { get: p._get_autoPurge, set: p._set_autoPurge }
@@ -904,30 +1259,34 @@ this.createjs = this.createjs||{};
// defaults and options
var options = {
- depth: false, // Disable the depth buffer as it isn't used.
- alpha: this._transparent, // Make the canvas background transparent.
- stencil: true,
+ depth: false, // nothing has depth
+ stencil: false, // while there's uses for this, we're not using any yet
+ premultipliedAlpha: this._transparent, // this is complicated, trust it
+
+ alpha: this._transparent,
antialias: this._antialias,
- premultipliedAlpha: this._premultiply, // Assume the drawing buffer contains colors with premultiplied alpha.
preserveDrawingBuffer: this._preserveBuffer
};
var gl = this._webGLContext = this._fetchWebGLContext(this.canvas, options);
if (!gl) { return null; }
- this.updateSimultaneousTextureCount(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
- this._maxTextureSlots = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
- this._createBuffers(gl);
- this._initTextures(gl);
-
gl.disable(gl.DEPTH_TEST);
+ gl.depthMask(false);
gl.enable(gl.BLEND);
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._premultiply);
- //gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
+ gl.clearColor(0.0, 0.0, 0.0, 0);
+
+ this._createBuffers();
+ this._initMaterials();
+ this._updateRenderMode("source-over");
- this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
- this.updateViewport(this._viewportWidth || this.canvas.width, this._viewportHeight || this.canvas.height);
+ this.updateViewport(this.canvas.width, this.canvas.height);
+ if (!this._directDraw) {
+ this._bufferTextureOutput = this.getRenderBufferTexture(this._viewportWidth, this._viewportHeight);
+ }
+
+ this.canvas._invalid = true;
}
} else {
this._webGLContext = null;
@@ -943,16 +1302,12 @@ this.createjs = this.createjs||{};
if (!this.canvas) { return; }
if (this.tickOnUpdate) { this.tick(props); }
this.dispatchEvent("drawstart");
- if (this.autoClear) { this.clear(); }
if (this._webGLContext) {
- // Use WebGL.
- this._batchDraw(this, this._webGLContext);
- if (this._autoPurge != -1 && !(this._drawID%((this._autoPurge/2)|0))) {
- this.purgeTextures(this._autoPurge);
- }
+ this.draw(this._webGLContext, false);
} else {
// Use 2D.
+ if (this.autoClear) { this.clear(); }
var ctx = this.canvas.getContext("2d");
ctx.save();
this.updateContext(ctx);
@@ -967,18 +1322,15 @@ this.createjs = this.createjs||{};
*/
p.clear = function () {
if (!this.canvas) { return; }
- if (StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- var cc = this._clearColor;
- var adjust = this._transparent ? cc.a : 1.0;
- // Use WebGL settings; adjust for pre multiplied alpha appropriate to scenario
- this._webGLContext.clearColor(cc.r * adjust, cc.g * adjust, cc.b * adjust, adjust);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._webGLContext.clearColor(cc.r, cc.g, cc.b, cc.a);
- } else {
- // Use 2D.
+
+ var gl = this._webGLContext;
+ if (!StageGL.isWebGLActive(gl)) { // Use 2D.
this.Stage_clear();
+ return;
}
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this._clearFrameBuffer(this._transparent ? this._clearColor.a : 1);
};
/**
@@ -994,13 +1346,52 @@ this.createjs = this.createjs||{};
* @return {Boolean} If the draw was handled by this function
*/
p.draw = function (context, ignoreCache) {
- if (context === this._webGLContext && StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- this._batchDraw(this, gl, ignoreCache);
- return true;
- } else {
+ var gl = this._webGLContext;
+ // 2D context fallback
+ if (!(context === this._webGLContext && StageGL.isWebGLActive(gl))) {
return this.Stage_draw(context, ignoreCache);
}
+
+ var storeBatchOutput = this._batchTextureOutput;
+ var storeBatchConcat = this._batchTextureConcat;
+ var storeBatchTemp = this._batchTextureTemp;
+
+ // Use WebGL
+ this._batchVertexCount = 0;
+ this._drawID++;
+
+ if (this._directDraw) {
+ this._batchTextureOutput = this;
+ if (this.autoClear) { this.clear(); }
+ } else {
+ this._batchTextureOutput = this._bufferTextureOutput;
+ this._batchTextureConcat = this._bufferTextureConcat;
+ this._batchTextureTemp = this._bufferTextureTemp;
+ }
+
+ this._updateRenderMode("source-over");
+ this._drawContent(this, ignoreCache);
+
+ if (!this._directDraw) {
+ if (this.autoClear) { this.clear(); }
+ this.batchReason = "finalOutput";
+ this._drawCover(null, this._batchTextureOutput);
+ }
+
+ // batches may generate or swap around textures. To be sure we capture them, store them back into buffer
+ this._bufferTextureOutput = this._batchTextureOutput;
+ this._bufferTextureConcat = this._batchTextureConcat;
+ this._bufferTextureTemp = this._batchTextureTemp;
+
+ this._batchTextureOutput = storeBatchOutput;
+ this._batchTextureConcat = storeBatchConcat;
+ this._batchTextureTemp = storeBatchTemp;
+
+ if (this._autoPurge !== -1 && !(this._drawID%((this._autoPurge/2)|0))) {
+ this.purgeTextures(this._autoPurge);
+ }
+
+ return true;
};
/**
@@ -1010,81 +1401,64 @@ this.createjs = this.createjs||{};
* @method cacheDraw
* @param {DisplayObject} target The object we're drawing into cache.
* For example, used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
- * @param {Array} filters The filters we're drawing into cache.
* @param {BitmapCache} manager The BitmapCache instance looking after the cache
* @return {Boolean} If the draw was handled by this function
*/
- p.cacheDraw = function (target, filters, manager) {
- if (StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- this._cacheDraw(gl, target, filters, manager);
- return true;
- } else {
+ p.cacheDraw = function (target, manager) {
+ // 2D context fallback
+ if (!StageGL.isWebGLActive(this._webGLContext)) {
return false;
}
- };
- /**
- * Blocks, or frees a texture "slot" on the GPU. Can be useful if you are overflowing textures. When overflowing
- * textures they are re-uploaded to the GPU every time they're encountered, this can be expensive with large textures.
- * By blocking the slot you reduce available slots, potentially increasing draw calls, but mostly you prevent a
- * texture being re-uploaded if it would have moved slots due to overflow.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * For example, block the slot a background image is stored in so there is less re-loading of that image.
- * @method protectTextureSlot
- * @param {Number} id The slot to be affected
- * @param {Boolean} [lock=false] Whether this slot is the one being locked.
- */
- p.protectTextureSlot = function (id, lock) {
- if (id > this._maxTextureSlots || id < 0) {
- throw "Slot outside of acceptable range";
- }
- this._slotBlacklist[id] = !!lock;
- };
+ var storeBatchOutput = this._batchTextureOutput;
+ var storeBatchConcat = this._batchTextureConcat;
+ var storeBatchTemp = this._batchTextureTemp;
- /**
- * Render textures can't draw into themselves so any item being used for renderTextures needs two to alternate between.
- * This function creates, gets, and toggles the render surface between the two.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method getTargetRenderTexture
- * @param {DisplayObject} target The object associated with the render textures, usually a cached object.
- * @param {Number} w The width to create the texture at.
- * @param {Number} h The height to create the texture at.
- * @return {Objet}
- * @todo fill in return type
- */
- p.getTargetRenderTexture = function (target, w, h) {
- var result, toggle = false;
- var gl = this._webGLContext;
- if (target.__lastRT !== undefined && target.__lastRT === target.__rtA) { toggle = true; }
- if (!toggle) {
- if (target.__rtA === undefined) {
- target.__rtA = this.getRenderBufferTexture(w, h);
- } else {
- if (w != target.__rtA._width || h != target.__rtA._height) {
- this.resizeTexture(target.__rtA, w, h);
- }
- this.setTextureParams(gl);
- }
- result = target.__rtA;
- } else {
- if (target.__rtB === undefined) {
- target.__rtB = this.getRenderBufferTexture(w, h);
- } else {
- if (w != target.__rtB._width || h != target.__rtB._height) {
- this.resizeTexture(target.__rtB, w, h);
- }
- this.setTextureParams(gl);
- }
- result = target.__rtB;
+ var filterCount = manager._filterCount, filtersLeft = filterCount;
+ var backupWidth = this._viewportWidth, backupHeight = this._viewportHeight;
+ this.updateViewport(manager._drawWidth, manager._drawHeight);
+
+ this._batchTextureOutput = (manager._filterCount%2) ? manager._bufferTextureConcat : manager._bufferTextureOutput;
+ this._batchTextureConcat = (manager._filterCount%2) ? manager._bufferTextureOutput : manager._bufferTextureConcat;
+ this._batchTextureTemp = manager._bufferTextureTemp;
+
+ var container = this._cacheContainer;
+ container.children = [target];
+ container.transformMatrix = this._alignTargetToCache(target, manager);
+
+ this._updateRenderMode("source-over");
+ this._drawContent(container, true);
+
+ // re-align buffers with fake filter passes to solve certain error cases
+ if (this.isCacheControlled) {
+ // post filter pass to place content into output buffer
+ //TODO: add in directDraw support for cache controlled StageGLs
+ filterCount++;
+ filtersLeft++;
+ } else if (manager._cacheCanvas !== ((manager._filterCount%2) ? this._batchTextureConcat : this._batchTextureOutput)) {
+ // pre filter pass to align output, may of become misaligned due to composite operations
+ filtersLeft++;
}
- if (!result) {
- throw "Problems creating render textures, known causes include using too much VRAM by not releasing WebGL texture instances";
+
+ while (filtersLeft) { //warning: pay attention to where filtersLeft is modified, this is a micro-optimization
+ var filter = manager._getGLFilter(filterCount - (filtersLeft--));
+ var swap = this._batchTextureConcat;
+ this._batchTextureConcat = this._batchTextureOutput;
+ this._batchTextureOutput = (this.isCacheControlled && filtersLeft === 0) ? this : swap;
+ this.batchReason = "filterPass";
+ this._drawCover(this._batchTextureOutput._frameBuffer, this._batchTextureConcat, filter);
}
- target.__lastRT = result;
- return result;
+
+ manager._bufferTextureOutput = this._batchTextureOutput;
+ manager._bufferTextureConcat = this._batchTextureConcat;
+ manager._bufferTextureTemp = this._batchTextureTemp;
+
+ this._batchTextureOutput = storeBatchOutput;
+ this._batchTextureConcat = storeBatchConcat;
+ this._batchTextureTemp = storeBatchTemp;
+
+ this.updateViewport(backupWidth, backupHeight);
+ return true;
};
/**
@@ -1098,10 +1472,10 @@ this.createjs = this.createjs||{};
* developer to ensure that a texture in use is not removed.
*
* Textures in use, or to be used again shortly, should not be removed. This is simply for performance reasons.
- * Removing a texture in use will cause the texture to have to be re-uploaded slowing rendering.
+ * Removing a texture in use will cause the texture to end up being re-uploaded slowing rendering.
* @method releaseTexture
- * @param {DisplayObject | Texture | Image | Canvas} item An object that used the texture to be discarded.
- * @param {Boolean} safe Should the release attempt to be "safe" and only delete this usage.
+ * @param {DisplayObject | WebGLTexture | Image | Canvas} item An object that used the texture to be discarded.
+ * @param {Boolean} [safe=false] Should the release attempt to be "safe" and only delete this usage.
*/
p.releaseTexture = function (item, safe) {
var i, l;
@@ -1110,7 +1484,7 @@ this.createjs = this.createjs||{};
// this is a container object
if (item.children) {
for (i = 0, l = item.children.length; i < l; i++) {
- this.releaseTexture(item.children[i]);
+ this.releaseTexture(item.children[i], safe);
}
}
@@ -1136,7 +1510,7 @@ this.createjs = this.createjs||{};
} else if (item._webGLRenderStyle === 1) {
// this is a SpriteSheet, we can't tell which image we used from the list easily so remove them all!
for (i = 0, l = item.spriteSheet._images.length; i < l; i++) {
- this.releaseTexture(item.spriteSheet._images[i]);
+ this.releaseTexture(item.spriteSheet._images[i], safe);
}
return;
}
@@ -1194,50 +1568,13 @@ this.createjs = this.createjs||{};
}
};
- /**
- * Try to set the max textures the system can handle. It should default to the hardware maximum, and lower values
- * may limit performance. Some devices have been known to mis-report their max textures, or you may need a standard
- * baseline cross devices for testing. Barring the previous suggestions, there is little need to call this function
- * as the library will automatically try to find the best value.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method updateSimultaneousTextureCount
- * @param {Number} [count=1] The number of textures intended for simultaneous loading.
- */
- p.updateSimultaneousTextureCount = function (count) {
- // TODO: DHG: make sure API works in all instances, may be some issues with buffers etc I haven't foreseen
- var gl = this._webGLContext;
- var success = false;
-
- if (count < 1 || isNaN(count)) { count = 1; }
- this._batchTextureCount = count;
-
- while (!success) {
- try {
- this._activeShader = this._fetchShaderProgram(gl);
- success = true;
- } catch(e) {
- if (this._batchTextureCount == 1) {
- throw "Cannot compile shader " + e;
- }
-
- this._batchTextureCount -= 4;
- if (this._batchTextureCount < 1) { this._batchTextureCount = 1; }
-
- if (this.vocalDebug) {
- console.log("Reducing desired texture count due to errors: " + this._batchTextureCount);
- }
- }
- }
- };
-
/**
* Update the WebGL viewport. Note that this does not update the canvas element's width/height, but
* the render surface's instead. This is necessary after manually resizing the canvas element on the DOM to avoid a
* up/down scaled render.
* @method updateViewport
- * @param {Integer} width The width of the render surface in pixels.
- * @param {Integer} height The height of the render surface in pixels.
+ * @param {int} width The width of the render surface in pixels.
+ * @param {int} height The height of the render surface in pixels.
*/
p.updateViewport = function (width, height) {
this._viewportWidth = width|0;
@@ -1252,16 +1589,20 @@ this.createjs = this.createjs||{};
// additionally we offset into they Y so the polygons are inside the camera's "clipping" plane
this._projectionMatrix = new Float32Array([
2 / this._viewportWidth, 0, 0, 0,
- 0, -2 / this._viewportHeight, 1, 0,
+ 0, -2 / this._viewportHeight, 0, 0,
0, 0, 1, 0,
- -1, 1, 0.1, 0
+ -1, 1, 0, 1
]);
- // create the flipped version for use with render texture flipping
- // DHG: this would be a slice/clone but some platforms don't offer them for Float32Array
- this._projectionMatrixFlip = new Float32Array([0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]);
- this._projectionMatrixFlip.set(this._projectionMatrix);
- this._projectionMatrixFlip[5] *= -1;
- this._projectionMatrixFlip[13] *= -1;
+
+ if (this._bufferTextureOutput !== null) {
+ this.resizeTexture(this._bufferTextureOutput, this._viewportWidth, this._viewportHeight);
+ }
+ if (this._bufferTextureConcat !== null) {
+ this.resizeTexture(this._bufferTextureConcat, this._viewportWidth, this._viewportHeight);
+ }
+ if (this._bufferTextureTemp !== null) {
+ this.resizeTexture(this._bufferTextureTemp, this._viewportWidth, this._viewportHeight);
+ }
}
};
@@ -1287,8 +1628,7 @@ this.createjs = this.createjs||{};
} else {
try {
targetShader = this._fetchShaderProgram(
- gl, "filter",
- filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY,
+ true, filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY,
filter.shaderParamSetup && filter.shaderParamSetup.bind(filter)
);
filter._builtShader = targetShader;
@@ -1330,10 +1670,12 @@ this.createjs = this.createjs||{};
* @protected
* @method resizeTexture
* @param {WebGLTexture} texture The GL Texture to be modified.
- * @param {uint} [width=1] The width of the texture in pixels, defaults to 1
- * @param {uint} [height=1] The height of the texture in pixels, defaults to 1
+ * @param {uint} width The width of the texture in pixels
+ * @param {uint} height The height of the texture in pixels
*/
p.resizeTexture = function (texture, width,height) {
+ if (texture.width === width && texture.height === height){ return; }
+
var gl = this._webGLContext;
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
@@ -1345,6 +1687,8 @@ this.createjs = this.createjs||{};
gl.UNSIGNED_BYTE, // type of texture(pixel color depth)
null // image data, we can do null because we're doing array data
);
+
+ // set its width and height for spoofing as an image and tracking
texture.width = width;
texture.height = height;
};
@@ -1352,24 +1696,22 @@ this.createjs = this.createjs||{};
/**
* Returns a base texture (see {{#crossLink "StageGL/getBaseTexture"}}{{/crossLink}}) for details. Also includes an
* attached and linked render buffer in `texture._frameBuffer`. RenderTextures can be thought of as an internal
- * canvas on the GPU that can be drawn to.
+ * canvas on the GPU that can be drawn to. Being internal to the GPU they are much faster than "offscreen canvases".
* @method getRenderBufferTexture
* @param {Number} w The width of the texture in pixels.
* @param {Number} h The height of the texture in pixels.
- * @return {Texture} the basic texture instance with a render buffer property.
+ * @return {WebGLTexture} the basic texture instance with a render buffer property.
*/
p.getRenderBufferTexture = function (w, h) {
var gl = this._webGLContext;
- // get the texture
var renderTexture = this.getBaseTexture(w, h);
if (!renderTexture) { return null; }
- // get the frame buffer
var frameBuffer = gl.createFramebuffer();
if (!frameBuffer) { return null; }
- // set its width and height for spoofing as an image
+ // set its width and height for spoofing as an image and tracking
renderTexture.width = w;
renderTexture.height = h;
@@ -1427,16 +1769,16 @@ this.createjs = this.createjs||{};
p.setClearColor = function (color) {
var r, g, b, a, output;
- if (typeof color == "string") {
- if (color.indexOf("#") == 0) {
- if (color.length == 4) {
+ if (typeof color === "string") {
+ if (color.indexOf("#") === 0) {
+ if (color.length === 4) {
color = "#" + color.charAt(1)+color.charAt(1) + color.charAt(2)+color.charAt(2) + color.charAt(3)+color.charAt(3)
}
r = Number("0x"+color.slice(1, 3))/255;
g = Number("0x"+color.slice(3, 5))/255;
b = Number("0x"+color.slice(5, 7))/255;
a = Number("0x"+color.slice(7, 9))/255;
- } else if (color.indexOf("rgba(") == 0) {
+ } else if (color.indexOf("rgba(") === 0) {
output = color.slice(5, -1).split(",");
r = Number(output[0])/255;
g = Number(output[1])/255;
@@ -1454,9 +1796,6 @@ this.createjs = this.createjs||{};
this._clearColor.g = g || 0;
this._clearColor.b = b || 0;
this._clearColor.a = a || 0;
-
- if (!this._webGLContext) { return; }
- this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
};
/**
@@ -1474,11 +1813,12 @@ this.createjs = this.createjs||{};
* @method _getSafeTexture
* @param {uint} [w=1] The width of the texture in pixels, defaults to 1
* @param {uint} [h=1] The height of the texture in pixels, defaults to 1
+ * @protected
*/
p._getSafeTexture = function (w, h) {
var texture = this.getBaseTexture(w, h);
- if(!texture) {
+ if (!texture) {
var msg = "Problem creating texture, possible cause: using too much VRAM, please try releasing texture memory";
(console.error && console.error(msg)) || console.log(msg);
@@ -1488,6 +1828,25 @@ this.createjs = this.createjs||{};
return texture;
};
+ /**
+ * Visually clear out the currently active FrameBuffer, does not rebind or adjust the frameBuffer in use.
+ * @method _getSafeTexture
+ * @param alpha
+ * @protected
+ */
+ p._clearFrameBuffer = function (alpha) {
+ var gl = this._webGLContext;
+ var cc = this._clearColor;
+
+ if (alpha > 0) { alpha = 1; }
+ if (alpha < 0) { alpha = 0; }
+
+ // Use WebGL settings; adjust for pre multiplied alpha appropriate to scenario
+ gl.clearColor(cc.r * alpha, cc.g * alpha, cc.b * alpha, alpha);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.clearColor(0, 0, 0, 0);
+ };
+
/**
* Sets up and returns the WebGL context for the canvas. May return undefined in error scenarios. These can include
* situations where the canvas element already has a context, 2D or GL.
@@ -1522,40 +1881,29 @@ this.createjs = this.createjs||{};
* filters. Once compiled, shaders are saved so. If the Shader code requires dynamic alterations re-run this function
* to generate a new shader.
* @method _fetchShaderProgram
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {String} [shaderName="regular"] Working values: "regular", "override", and "filter". Which type of shader to build.
+ * @param {Boolean} coverShader Is this a per object shader or a cover shader
* Filter and override both accept the custom params. Regular and override have all features. Filter is a special case reduced feature shader meant to be over-ridden.
- * @param {String} [customVTX] Extra vertex shader information to replace a regular draw, see
+ * @param {String | undefined} [customVTX=undefined] Extra vertex shader information to replace a regular draw, see
* {{#crossLink "StageGL/COVER_VERTEX_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
- * @param {String} [customFRAG] Extra fragment shader information to replace a regular draw, see
+ * @param {String | undefined} [customFRAG=undefined] Extra fragment shader information to replace a regular draw, see
* {{#crossLink "StageGL/COVER_FRAGMENT_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
- * @param {Function} [shaderParamSetup] Function to run so custom shader parameters can get applied for the render.
+ * @param {Function | undefined} [shaderParamSetup=undefined] Function to run so custom shader parameters can get applied for the render.
* @protected
* @return {WebGLProgram} The compiled and linked shader
*/
- p._fetchShaderProgram = function (gl, shaderName, customVTX, customFRAG, shaderParamSetup) {
+ p._fetchShaderProgram = function (coverShader, customVTX, customFRAG, shaderParamSetup) {
+ var gl = this._webGLContext;
+
gl.useProgram(null); // safety to avoid collisions
// build the correct shader string out of the right headers and bodies
var targetFrag, targetVtx;
- switch (shaderName) {
- case "filter":
- targetVtx = StageGL.COVER_VERTEX_HEADER + (customVTX || StageGL.COVER_VERTEX_BODY);
- targetFrag = StageGL.COVER_FRAGMENT_HEADER + (customFRAG || StageGL.COVER_FRAGMENT_BODY);
- break;
- case "particle": //TODO
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.PARTICLE_VERTEX_BODY;
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.PARTICLE_FRAGMENT_BODY;
- break;
- case "override":
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + (customVTX || StageGL.REGULAR_VERTEX_BODY);
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + (customFRAG || StageGL.REGULAR_FRAGMENT_BODY);
- break;
- case "regular":
- default:
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.REGULAR_VERTEX_BODY;
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.REGULAR_FRAGMENT_BODY;
- break;
+ if (coverShader) {
+ targetVtx = StageGL.COVER_VERTEX_HEADER + (customVTX || StageGL.COVER_VERTEX_BODY);
+ targetFrag = StageGL.COVER_FRAGMENT_HEADER + (customFRAG || StageGL.COVER_FRAGMENT_BODY);
+ } else {
+ targetVtx = StageGL.REGULAR_VERTEX_HEADER + (customVTX || StageGL.REGULAR_VERTEX_BODY);
+ targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + (customFRAG || StageGL.REGULAR_FRAGMENT_BODY);
}
// create the separate vars
@@ -1567,7 +1915,6 @@ this.createjs = this.createjs||{};
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
- shaderProgram._type = shaderName;
// check compile status
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
@@ -1577,58 +1924,44 @@ this.createjs = this.createjs||{};
// set up the parameters on the shader
gl.useProgram(shaderProgram);
- switch (shaderName) {
- case "filter":
- // get the places in memory the shader is stored so we can feed information into them
- // then save it off on the shader because it's so tied to the shader itself
- shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
- gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
- shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
- gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
+ // get the places in memory the shader is stored so we can feed information into them
+ // then save it off on the shader because it's so tied to the shader itself
+ shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
+ gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
- shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
- gl.uniform1i(shaderProgram.samplerUniform, 0);
+ shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
+ gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
- shaderProgram.uprightUniform = gl.getUniformLocation(shaderProgram, "uUpright");
- gl.uniform1f(shaderProgram.uprightUniform, 0);
+ if (coverShader) {
+ shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
+ gl.uniform1i(shaderProgram.samplerUniform, 0);
- // if there's some custom attributes be sure to hook them up
- if (shaderParamSetup) {
- shaderParamSetup(gl, this, shaderProgram);
- }
- break;
- case "override":
- case "particle":
- case "regular":
- default:
- // get the places in memory the shader is stored so we can feed information into them
- // then save it off on the shader because it's so tied to the shader itself
- shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
- gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
-
- shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
- gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
-
- shaderProgram.textureIndexAttribute = gl.getAttribLocation(shaderProgram, "textureIndex");
- gl.enableVertexAttribArray(shaderProgram.textureIndexAttribute);
-
- shaderProgram.alphaAttribute = gl.getAttribLocation(shaderProgram, "objectAlpha");
- gl.enableVertexAttribArray(shaderProgram.alphaAttribute);
-
- var samplers = [];
- for (var i = 0; i < this._batchTextureCount; i++) {
- samplers[i] = i;
- }
+ // if there's some custom attributes be sure to hook them up
+ if (shaderParamSetup) {
+ shaderParamSetup(gl, this, shaderProgram);
+ }
+ } else {
+ shaderProgram.textureIndexAttribute = gl.getAttribLocation(shaderProgram, "textureIndex");
+ gl.enableVertexAttribArray(shaderProgram.textureIndexAttribute);
+
+ shaderProgram.alphaAttribute = gl.getAttribLocation(shaderProgram, "objectAlpha");
+ gl.enableVertexAttribArray(shaderProgram.alphaAttribute);
+
+ var samplers = [];
+ for (var i = 0; i < this._gpuTextureCount; i++) {
+ samplers[i] = i;
+ }
+ shaderProgram.samplerData = samplers;
- shaderProgram.samplerData = samplers;
- shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
- gl.uniform1iv(shaderProgram.samplerUniform, samplers);
+ shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
+ gl.uniform1iv(shaderProgram.samplerUniform, shaderProgram.samplerData);
- shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix");
- break;
+ shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix");
}
+ shaderProgram._type = coverShader ? "cover" : "batch";
+
gl.useProgram(this._activeShader);
return shaderProgram;
};
@@ -1643,17 +1976,20 @@ this.createjs = this.createjs||{};
* @protected
*/
p._createShader = function (gl, type, str) {
- // inject the static number
- str = str.replace(/\{\{count}}/g, this._batchTextureCount);
+ var textureCount = this._batchTextureCount;
- // resolve issue with no dynamic samplers by creating correct samplers in if else chain
- // TODO: WebGL 2.0 does not need this support
- var insert = "";
- for (var i = 1; i= 0) {
this._textureDictionary[texture._storeID] = undefined;
for (var n in this._textureIDs) {
- if (this._textureIDs[n] == texture._storeID) { delete this._textureIDs[n]; }
+ if (this._textureIDs[n] === texture._storeID) { delete this._textureIDs[n]; }
}
var data = texture._imageData;
- for (var i=data.length-1; i>=0; i--) { data[i]._storeID = undefined; }
+ if (data) {
+ for (var i=data.length-1; i>=0; i--) { data[i]._storeID = undefined; }
+ }
texture._imageData = texture._storeID = undefined;
}
@@ -1978,241 +2341,177 @@ this.createjs = this.createjs||{};
};
/**
- * Store or restore current batch textures into a backup array
- * @method _backupBatchTextures
- * @param {Boolean} restore Perform a restore instead of a store.
- * @param {Array} [target=this._backupTextures] Where to perform the backup, defaults to internal backup.
+ * Small utility function to keep internal API consistent and set the uniforms for a dual texture cover render
+ * @method _setCoverMixShaderParams
+ * @param {WebGLRenderingContext} gl The context where the drawing takes place
+ * @param stage unused
+ * @param shaderProgram unused
* @protected
*/
- p._backupBatchTextures = function (restore, target) {
- var gl = this._webGLContext;
-
- if (!this._backupTextures) { this._backupTextures = []; }
- if (target === undefined) { target = this._backupTextures; }
-
- for (var i=0; i 0) {
- this._drawBuffers(gl);
+ p._updateRenderMode = function (newMode) {
+ if ( newMode === null || newMode === undefined){ newMode = "source-over"; }
+
+ var blendSrc = StageGL.BLEND_SOURCES[newMode];
+ if (blendSrc === undefined) {
+ if (this.vocalDebug){ console.log("Unknown compositeOperation ["+ newMode +"], reverting to default"); }
+ blendSrc = StageGL.BLEND_SOURCES[newMode = "source-over"];
}
- this._isDrawing++;
- this._drawID++;
- this.batchCardCount = 0;
- this.depth = 0;
+ if (this._renderMode === newMode) { return; }
- this._appendToBatchGroup(sceneGraph, gl, new createjs.Matrix2D(), this.alpha, ignoreCache);
+ var gl = this._webGLContext;
+ var shaderData = this._builtShaders[newMode];
+ if (shaderData === undefined) {
+ try {
+ shaderData = this._builtShaders[newMode] = {
+ eqRGB: gl[blendSrc.eqRGB || "FUNC_ADD"],
+ srcRGB: gl[blendSrc.srcRGB || "ONE"],
+ dstRGB: gl[blendSrc.dstRGB || "ONE_MINUS_SRC_ALPHA"],
+ eqA: gl[blendSrc.eqA || "FUNC_ADD"],
+ srcA: gl[blendSrc.srcA || "ONE"],
+ dstA: gl[blendSrc.dstA || "ONE_MINUS_SRC_ALPHA"],
+ immediate: blendSrc.shader !== undefined,
+ shader: (blendSrc.shader || this._builtShaders["source-over"] === undefined) ?
+ this._fetchShaderProgram(
+ true, undefined, blendSrc.shader,
+ this._setCoverMixShaderParams
+ ) : this._builtShaders["source-over"].shader // re-use source-over when we don't need a new shader
+ };
+ if (blendSrc.shader) { shaderData.shader._name = newMode; }
+ } catch (e) {
+ this._builtShaders[newMode] = undefined;
+ console && console.log("SHADER SWITCH FAILURE", e);
+ return;
+ }
+ }
- this.batchReason = "drawFinish";
- this._drawBuffers(gl); // <--------------------------------------------------------
- this._isDrawing--;
+ if (shaderData.immediate && this._directDraw) {
+ if (this.vocalDebug) { console.log("Illegal compositeOperation ["+ newMode +"] due to StageGL.directDraw = true, reverting to default"); }
+ return;
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureOutput._frameBuffer);
+
+ this.batchReason = "shaderSwap";
+ this._renderBatch(); // <--------------------------------------------------------------------------------
+
+ this._renderMode = newMode;
+ this._immediateRender = shaderData.immediate;
+ gl.blendEquationSeparate(shaderData.eqRGB, shaderData.eqA);
+ gl.blendFuncSeparate(shaderData.srcRGB, shaderData.dstRGB, shaderData.srcA, shaderData.dstA);
};
/**
- * Perform the drawing process to fill a specific cache texture, including applying filters.
- * @method _cacheDraw
- * @param {DisplayObject} target The object we're drawing into the cache. For example, used for drawing the cache
- * (to prevent it from simply drawing an existing cache back into itself).
- * @param {Array} filters The filters we're drawing into cache.
- * @param {BitmapCache} manager The BitmapCache instance looking after the cache
+ * Helper function for the standard content draw
+ * @method _drawContent
+ * @param {Stage | Container} content
+ * @param {Boolean} ignoreCache
* @protected
*/
- p._cacheDraw = function (gl, target, filters, manager) {
- /*
- Implicitly there are 4 modes to this function: filtered-sameContext, filtered-uniqueContext, sameContext, uniqueContext.
- Each situation must be handled slightly differently as 'sameContext' or 'uniqueContext' define how the output works,
- one drawing directly into the main context and the other drawing into a stored renderTexture respectively.
- When the draw is a 'filtered' draw, the filters are applied sequentially and will draw into saved textures repeatedly.
- Once the final filter is done the final output is treated depending upon whether it is a same or unique context.
- The internal complexity comes from reducing over-draw, shared code, and issues like textures needing to be flipped
- sometimes when written to render textures.
- */
- var renderTexture;
- var shaderBackup = this._activeShader;
- var blackListBackup = this._slotBlacklist;
- var lastTextureSlot = this._maxTextureSlots-1;
- var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
-
- // protect the last slot so that we have somewhere to bind the renderTextures so it doesn't get upset
- this.protectTextureSlot(lastTextureSlot, true);
-
- // create offset container for drawing item
- var mtx = target.getMatrix();
- mtx = mtx.clone();
- mtx.scale(1/manager.scale, 1/manager.scale);
- mtx = mtx.invert();
- mtx.translate(-manager.offX/manager.scale*target.scaleX, -manager.offY/manager.scale*target.scaleY);
- var container = this._cacheContainer;
- container.children = [target];
- container.transformMatrix = mtx;
+ p._drawContent = function (content, ignoreCache) {
+ var gl = this._webGLContext;
- this._backupBatchTextures(false);
+ this._activeShader = this._mainShader;
- if (filters && filters.length) {
- this._drawFilters(target, filters, manager);
- } else {
- // is this for another stage or mine?
- if (this.isCacheControlled) {
- // draw item to canvas I -> C
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
- } else {
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- target.cacheCanvas = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- renderTexture = target.cacheCanvas;
-
- // draw item to render texture I -> T
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
- this.updateViewport(manager._drawWidth, manager._drawHeight);
- this._projectionMatrix = this._projectionMatrixFlip;
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
- }
- }
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureOutput._frameBuffer);
+ gl.clear(gl.COLOR_BUFFER_BIT);
- this._backupBatchTextures(true);
+ this._appendToBatch(content, new createjs.Matrix2D(), this.alpha, ignoreCache);
- this.protectTextureSlot(lastTextureSlot, false);
- this._activeShader = shaderBackup;
- this._slotBlacklist = blackListBackup;
+ this.batchReason = "contentEnd";
+ this._renderBatch();
};
/**
- * Sub portion of _cacheDraw, split off for readability. Do not call independently.
- * @method _drawFilters
- * @param {DisplayObject} target The object we're drawing with a filter.
- * @param {Array} filters The filters we're drawing into cache.
- * @param {BitmapCache} manager The BitmapCache instance looking after the cache
+ * Used to draw one or more textures potentially using a filter into the supplied buffer.
+ * Mostly used for caches, filters, and outputting final render frames.
+ * Draws `dst` into `out` after applying `srcFilter` depending on its current value.
+ * @method _drawCover
+ * @param {WebGLFramebuffer} out Buffer to draw the results into (null is the canvas element)
+ * @param {WebGLTexture} dst Base texture layer aka "destination" in image blending terminology
+ * @param {WebGLTexture | Filter} [srcFilter = undefined] Modification parameter for the draw. If a texture, the
+ * current _renderMode applies it as a "source" image. If a Filter, the filter is applied to the dst with its params.
+ * @protected
*/
- p._drawFilters = function (target, filters, manager) {
+ p._drawCover = function (out, dst, srcFilter) {
var gl = this._webGLContext;
- var renderTexture;
- var lastTextureSlot = this._maxTextureSlots-1;
- var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
-
- var container = this._cacheContainer;
- var filterCount = filters.length;
- // we don't know which texture slot we're dealing with previously and we need one out of the way
- // once we're using that slot activate it so when we make and bind our RenderTexture it's safe there
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, out);
+ if (out !== null){ gl.clear(gl.COLOR_BUFFER_BIT); }
- // draw item to render texture I -> T
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
- this.updateViewport(manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
-
- // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, renderTexture);
+ gl.bindTexture(gl.TEXTURE_2D, dst);
this.setTextureParams(gl);
- var flipY = false;
- var i = 0, filter = filters[i];
- do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
-
- // swap to correct shader
- this._activeShader = this.getFilterShader(filter);
- if (!this._activeShader) { continue; }
-
- // now the old result is stored in slot 0, make a new render texture
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
-
- // draw result to render texture R -> T
- gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, flipY);
-
- // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, renderTexture);
- this.setTextureParams(gl);
-
- // use flipping to keep things upright, things already cancel out on a single filter
- // this needs to be here as multiPass is not accurate to _this_ frame until after shader acquisition
- if (filterCount > 1 || filters[0]._multiPass) {
- flipY = !flipY;
+ if (srcFilter instanceof createjs.Filter) {
+ this._activeShader = this.getFilterShader(srcFilter);
+ } else {
+ if (srcFilter instanceof WebGLTexture) {
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, srcFilter);
+ this.setTextureParams(gl);
+ } else if (srcFilter !== undefined && this.vocalDebug) {
+ console.log("Unknown data handed to function: ", srcFilter);
}
+ this._activeShader = this._builtShaders[this._renderMode].shader;
+ }
- // work through the multipass if it's there, otherwise move on
- filter = filter._multiPass !== null ? filter._multiPass : filters[++i];
- } while (filter);
-
- // is this for another stage or mine
- if (this.isCacheControlled) {
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
+ this._renderCover();
+ };
- // draw result to canvas R -> C
- this._activeShader = this.getFilterShader(this);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, flipY);
+ /**
+ * Returns a matrix that can be used to counter position the `target` so that it fits and scales to the `manager`
+ * @param {DisplayObject} target The object to be counter positioned
+ * @param {BitmapCache} manager The cache manager to be aligned to
+ * @returns {Matrix2D} The matrix that can be used used to counter position the container
+ * @method _alignTargetToCache
+ * @private
+ */
+ p._alignTargetToCache = function(target, manager) {
+ if (manager._counterMatrix === null) {
+ manager._counterMatrix = target.getMatrix();
} else {
- //TODO: DHG: this is less than ideal. A flipped initial render for this circumstance might help. Adjust the perspective matrix?
- if (flipY) {
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
-
- this._activeShader = this.getFilterShader(this);
- gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, !flipY);
- }
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
-
- // make sure the last texture is the active thing to draw
- target.cacheCanvas = renderTexture;
+ target.getMatrix(manager._counterMatrix)
}
+
+ var mtx = manager._counterMatrix;
+ mtx.scale(1/manager.scale, 1/manager.scale);
+ mtx = mtx.invert();
+ mtx.translate(-manager.offX/manager.scale*target.scaleX, -manager.offY/manager.scale*target.scaleY);
+
+ return mtx;
};
/**
* Add all the contents of a container to the pending buffers, called recursively on each container. This may
* trigger a draw if a buffer runs out of space. This is the main workforce of the render loop.
- * @method _appendToBatchGroup
+ * @method _appendToBatch
* @param {Container} container The {{#crossLink "Container"}}{{/crossLink}} that contains everything to be drawn.
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
* @param {Matrix2D} concatMtx The effective (concatenated) transformation matrix when beginning this container
* @param {Number} concatAlpha The effective (concatenated) alpha when beginning this container
- * @param {Boolean} ignoreCache Don't use an element's cache during this draw
+ * @param {Boolean} [ignoreCache=false] Don't use an element's cache during this draw
* @protected
*/
- p._appendToBatchGroup = function (container, gl, concatMtx, concatAlpha, ignoreCache) {
+ p._appendToBatch = function (container, concatMtx, concatAlpha, ignoreCache) {
+ var gl = this._webGLContext;
+
// sort out shared properties
- if (!container._glMtx) { container._glMtx = new createjs.Matrix2D(); }
var cMtx = container._glMtx;
cMtx.copy(concatMtx);
- if (container.transformMatrix) {
+ if (container.transformMatrix !== null) {
cMtx.appendMatrix(container.transformMatrix);
} else {
cMtx.appendTransform(
@@ -2223,6 +2522,11 @@ this.createjs = this.createjs||{};
);
}
+ var previousRenderMode = this._renderMode;
+ if (container.compositeOperation) {
+ this._updateRenderMode(container.compositeOperation);
+ }
+
// sub components of figuring out the position an object holds
var subL, subT, subR, subB;
@@ -2230,28 +2534,53 @@ this.createjs = this.createjs||{};
var l = container.children.length;
for (var i = 0; i < l; i++) {
var item = container.children[i];
+ var useCache = (!ignoreCache && item.cacheCanvas) || false;
- if (!(item.visible && concatAlpha)) { continue; }
- if (!item.cacheCanvas || ignoreCache) {
+ if (!(item.visible && concatAlpha > 0.0035)) { continue; }
+
+ if (useCache === false) {
if (item._updateState){
item._updateState();
}
- if (item.children) {
- this._appendToBatchGroup(item, gl, cMtx, item.alpha * concatAlpha);
- continue;
+
+ if(!ignoreCache && item.cacheCanvas === null && item.filters !== null && item.filters.length) {
+ var bounds;
+ if (item.bitmapCache === null) {
+ bounds = item.getBounds();
+ item.bitmapCache = new createjs.BitmapCache();
+ item.bitmapCache._autoGenerated = true;
+ }
+ if (item.bitmapCache._autoGenerated) {
+ this.batchReason = "cachelessFilterInterupt";
+ this._renderBatch(); // <----------------------------------------------------
+
+ var shaderBackup = this._activeShader;
+ bounds = bounds || item.getBounds();
+ item.bitmapCache.define(item, bounds.x, bounds.y, bounds.width, bounds.height, 1, {useGL:this});
+ useCache = item.bitmapCache._cacheCanvas;
+
+ this._activeShader = shaderBackup;
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureOutput._frameBuffer);
+ }
}
}
+ if (useCache === false && item.children) {
+ this._appendToBatch(item, cMtx, item.alpha * concatAlpha);
+ continue;
+ }
+
+ if (item.compositeOperation !== null) {
+ this._updateRenderMode(item.compositeOperation);
+ }
+
// check for overflowing batch, if yes then force a render
- // TODO: DHG: consider making this polygon count dependant for things like vector draws
- if (this.batchCardCount+1 > this._maxCardsPerBatch) {
+ if (this._batchVertexCount + StageGL.INDICIES_PER_CARD > this._maxBatchVertexCount) {
this.batchReason = "vertexOverflow";
- this._drawBuffers(gl); // <------------------------------------------------------------
- this.batchCardCount = 0;
+ this._renderBatch(); // <------------------------------------------------------------
}
// keep track of concatenated position
- if (!item._glMtx) { item._glMtx = new createjs.Matrix2D(); }
var iMtx = item._glMtx;
iMtx.copy(cMtx);
if (item.transformMatrix) {
@@ -2266,16 +2595,20 @@ this.createjs = this.createjs||{};
}
var uvRect, texIndex, image, frame, texture, src;
- var useCache = item.cacheCanvas && !ignoreCache;
// get the image data, or abort if not present
- if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
- image = (ignoreCache?false:item.cacheCanvas) || item.image;
- } else if (item._webGLRenderStyle === 1) { // SPRITE
- frame = item.spriteSheet.getFrame(item.currentFrame); //TODO: Faster way?
+ // BITMAP / Cached Canvas
+ if (item._webGLRenderStyle === 2 || useCache !== false) {
+ image = useCache === false ? item.image : useCache;
+
+ // SPRITE
+ } else if (item._webGLRenderStyle === 1) {
+ frame = item.spriteSheet.getFrame(item.currentFrame);
if (frame === null) { continue; }
image = frame.image;
- } else { // MISC (DOM objects render themselves later)
+
+ // MISC (DOM objects render themselves later)
+ } else {
continue;
}
if (!image) { continue; }
@@ -2306,15 +2639,16 @@ this.createjs = this.createjs||{};
texIndex = texture._activeIndex;
image._drawID = this._drawID;
- if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
- if (!useCache && item.sourceRect) {
+ // BITMAP / Cached Canvas
+ if (item._webGLRenderStyle === 2 || useCache !== false) {
+ if (useCache === false && item.sourceRect) {
// calculate uvs
if (!item._uvRect) { item._uvRect = {}; }
src = item.sourceRect;
uvRect = item._uvRect;
- uvRect.t = (src.y)/image.height;
+ uvRect.t = 1 - ((src.y)/image.height);
uvRect.l = (src.x)/image.width;
- uvRect.b = (src.y + src.height)/image.height;
+ uvRect.b = 1 - ((src.y + src.height)/image.height);
uvRect.r = (src.x + src.width)/image.width;
// calculate vertices
@@ -2324,16 +2658,18 @@ this.createjs = this.createjs||{};
// calculate uvs
uvRect = StageGL.UV_RECT;
// calculate vertices
- if (useCache) {
+ if (useCache === false) {
+ subL = 0; subT = 0;
+ subR = image.width+subL; subB = image.height+subT;
+ } else {
src = item.bitmapCache;
subL = src.x+(src._filterOffX/src.scale); subT = src.y+(src._filterOffY/src.scale);
subR = (src._drawWidth/src.scale)+subL; subB = (src._drawHeight/src.scale)+subT;
- } else {
- subL = 0; subT = 0;
- subR = image.width+subL; subB = image.height+subT;
}
}
- } else if (item._webGLRenderStyle === 1) { // SPRITE
+
+ // SPRITE
+ } else if (item._webGLRenderStyle === 1) {
var rect = frame.rect;
// calculate uvs
@@ -2348,8 +2684,8 @@ this.createjs = this.createjs||{};
}
// These must be calculated here else a forced draw might happen after they're set
- var offV1 = this.batchCardCount*StageGL.INDICIES_PER_CARD; // offset for 1 component vectors
- var offV2 = offV1*2; // offset for 2 component vectors
+ var offV1 = this._batchVertexCount; // offset for 1 component vectors
+ var offV2 = offV1*2; // offset for 2 component vectors
//DHG: See Matrix2D.transformPoint for why this math specifically
// apply vertices
@@ -2374,21 +2710,67 @@ this.createjs = this.createjs||{};
// apply alpha
alphas[offV1] = alphas[offV1+1] = alphas[offV1+2] = alphas[offV1+3] = alphas[offV1+4] = alphas[offV1+5] = item.alpha * concatAlpha;
- this.batchCardCount++;
+ this._batchVertexCount += StageGL.INDICIES_PER_CARD;
+
+ if (this._immediateRender) {
+ this._immediateBatchRender();
+ }
+ }
+
+ if (this._renderMode !== previousRenderMode) {
+ this._updateRenderMode(previousRenderMode);
}
};
+ /**
+ * The shader or effect needs to be drawn immediately, sub function of `_appendToBatch`
+ * @method _immediateBatchRender
+ * @protected
+ */
+ p._immediateBatchRender = function() {
+ var gl = this._webGLContext;
+
+ if (this._batchTextureConcat === null){
+ this._batchTextureConcat = this.getRenderBufferTexture(this._viewportWidth, this._viewportHeight);
+ } else {
+ this.resizeTexture(this._batchTextureConcat, this._viewportWidth, this._viewportHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureConcat._frameBuffer);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+ if (this._batchTextureTemp === null){
+ this._batchTextureTemp = this.getRenderBufferTexture(this._viewportWidth, this._viewportHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureTemp._frameBuffer);
+ } else {
+ this.resizeTexture(this._batchTextureTemp, this._viewportWidth, this._viewportHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureTemp._frameBuffer);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+
+ var swap = this._batchTextureOutput;
+ this._batchTextureOutput = this._batchTextureConcat;
+ this._batchTextureConcat = swap;
+
+ this._activeShader = this._mainShader;
+ this.batchReason = "immediatePrep";
+ this._renderBatch();//<-----------------------------------------------------------------------------------------
+
+ this.batchReason = "immediateResults";
+ this._drawCover(this._batchTextureOutput._frameBuffer, this._batchTextureConcat, this._batchTextureTemp);
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._batchTextureOutput._frameBuffer);
+ };
+
/**
* Draws all the currently defined cards in the buffer to the render surface.
- * @method _drawBuffers
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @method _renderBatch
* @protected
*/
- p._drawBuffers = function (gl) {
- if (this.batchCardCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content.
+ p._renderBatch = function () {
+ if (this._batchVertexCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content.
+ var gl = this._webGLContext;
if (this.vocalDebug) {
- console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason);
+ console.log("Batch["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason);
}
var shaderProgram = this._activeShader;
var vertexPositionBuffer = this._vertexPositionBuffer;
@@ -2423,31 +2805,27 @@ this.createjs = this.createjs||{};
this.setTextureParams(gl, texture.isPOT);
}
- gl.drawArrays(gl.TRIANGLES, 0, this.batchCardCount*StageGL.INDICIES_PER_CARD);
+ gl.drawArrays(gl.TRIANGLES, 0, this._batchVertexCount);
+
+ this._batchVertexCount = 0;
this._batchID++;
};
/**
- * Draws a card that covers the entire render surface. Mainly used for filters.
- * @method _drawBuffers
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {Boolean} flipY Covers are used for things like RenderTextures and because of 3D vs Canvas space this can
- * end up meaning the `y` space sometimes requires flipping in the render.
+ * Draws a card that covers the entire render surface. Mainly used for filters and composite operations.
+ * @method _renderCover
* @protected
*/
- p._drawCover = function (gl, flipY) {
- if (this._isDrawing > 0) {
- this._drawBuffers(gl);
- }
+ p._renderCover = function () {
+ var gl = this._webGLContext;
if (this.vocalDebug) {
- console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ "Cover");
+ console.log("Cover["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason);
}
var shaderProgram = this._activeShader;
var vertexPositionBuffer = this._vertexPositionBuffer;
var uvPositionBuffer = this._uvPositionBuffer;
- gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
@@ -2455,12 +2833,12 @@ this.createjs = this.createjs||{};
gl.bufferSubData(gl.ARRAY_BUFFER, 0, StageGL.COVER_VERT);
gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer);
gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, flipY?StageGL.COVER_UV_FLIP:StageGL.COVER_UV);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, StageGL.COVER_UV);
gl.uniform1i(shaderProgram.samplerUniform, 0);
- gl.uniform1f(shaderProgram.uprightUniform, flipY?0:1);
gl.drawArrays(gl.TRIANGLES, 0, StageGL.INDICIES_PER_CARD);
+ this._batchID++; // while this isn't a batch, this fixes issues with expected textures in expected places
};
createjs.StageGL = createjs.promote(StageGL, "Stage");
diff --git a/src/easeljs/filters/AberrationFilter.js b/src/easeljs/filters/AberrationFilter.js
new file mode 100644
index 000000000..9907d64a4
--- /dev/null
+++ b/src/easeljs/filters/AberrationFilter.js
@@ -0,0 +1,179 @@
+/*
+ * AberrationFilter
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ * Copyright (c) 2010 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module EaselJS
+ */
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Separates and pushes each of the colour channels apart. I.E. shift the red channel slightly left.
+ * Allows specifying the direction and the ammount it affects each channel. Great for computer glitches and VCR like
+ * effects.
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
+ * @class AberrationFilter
+ * @extends Filter
+ * @constructor
+ * @param {Number} [xDir=0] Movement in x at a multiplier of 1, specified in pixels.
+ * @param {Number} [yDir=0] Movement in y at a multiplier of 1, specified in pixels.
+ * @param {Number} [redMultiplier=0] Multiplier for the movement of the Red channel. Negative values allowed.
+ * @param {Number} [greenMultiplier=0] Multiplier for the movement of the Green channel. Negative values allowed.
+ * @param {Number} [blueMultiplier=0] Multiplier for the movement of the Blue channel. Negative values allowed.
+ * @param {Number} [originalMix=0] Amount of original image to keep, 0-1.
+ * @param {Boolean} [alphaMax=false] Calculate combined alpha using maximum alpha available. Creates a stronger image.
+ **/
+ function AberrationFilter(xDir, yDir, redMultiplier, greenMultiplier, blueMultiplier, originalMix, alphaMax) {
+ this.Filter_constructor();
+
+ // public properties:
+ this.xDir = Number(xDir) || 0;
+ this.yDir = Number(yDir) || 0;
+
+ this.redMultiplier = Number(redMultiplier) || 0;
+ this.greenMultiplier = Number(greenMultiplier) || 0;
+ this.blueMultiplier = Number(blueMultiplier) || 0;
+
+ this.originalMix = Math.min(Math.max(originalMix, 0), 1) || 0;
+ this._alphaMax = Boolean(alphaMax);
+
+ this.FRAG_SHADER_BODY = (
+ "uniform vec2 uColorDirection;" +
+ "uniform vec3 uColorMultiplier;" +
+ "uniform vec2 uExtraProps;" +
+
+ "void main(void) {" +
+ "vec4 sample = texture2D(" +
+ "uSampler, " +
+ "vTextureCoord" +
+ ");" +
+ "vec4 rSample = texture2D(" +
+ "uSampler, " +
+ "vTextureCoord + (uColorDirection * uColorMultiplier.r)" +
+ ");" +
+ "vec4 gSample = texture2D(" +
+ "uSampler, " +
+ "vTextureCoord + (uColorDirection * uColorMultiplier.g)" +
+ ");" +
+ "vec4 bSample = texture2D(" +
+ "uSampler, " +
+ "vTextureCoord + (uColorDirection * uColorMultiplier.b)" +
+ ");" +
+
+ "vec4 result = vec4(" +
+ "rSample.r*rSample.a, " +
+ "gSample.g*gSample.a, " +
+ "bSample.b*bSample.a, " +
+ (alphaMax ?
+ "max(rSample.a, max(gSample.a, max(bSample.a, sample.a)))" :
+ "(rSample.a + gSample.a + bSample.a) / 3.0"
+ ) +
+ ");" +
+ "gl_FragColor = mix(result, sample, uExtraProps[0]*sample.a);" +
+ "}"
+ );
+
+ }
+ var p = createjs.extend(AberrationFilter, createjs.Filter);
+
+
+// public methods:
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {
+
+ gl.uniform2f(
+ gl.getUniformLocation(shaderProgram, "uColorDirection"),
+ this.xDir*(1/stage._viewportWidth), this.yDir*(1/stage._viewportHeight)
+ );
+
+ gl.uniform3f(
+ gl.getUniformLocation(shaderProgram, "uColorMultiplier"),
+ -this.redMultiplier,
+ -this.greenMultiplier,
+ -this.blueMultiplier
+ );
+
+ gl.uniform2f(
+ gl.getUniformLocation(shaderProgram, "uExtraProps"),
+ this.originalMix, 0
+ );
+ };
+
+// private methods:
+ p._applyFilter = function(imageData) {
+ var refPixels = imageData.data.slice();
+ var outPixels = imageData.data;
+ var width = imageData.width;
+ var height = imageData.height;
+ var offset, pixel;
+
+ for (var i=0; i= width) { redX = width-1; }
+ if (redY < 0) { redY = 0; }
+ if (redY >= height) { redY = height-1; }
+
+ if (grnX < 0) { grnX = 0; }
+ if (grnX >= width) { grnX = width-1; }
+ if (grnY < 0) { grnY = 0; }
+ if (grnY >= height) { grnY = height-1; }
+
+ if (bluX < 0) { bluX = 0; }
+ if (bluX >= width) { bluX = width-1; }
+ if (bluY < 0) { bluY = 0; }
+ if (bluY >= height) { bluY = height-1; }
+
+ var redPixel = ((redY*width)+redX)*4;
+ var grnPixel = ((grnY*width)+grnX)*4;
+ var bluPixel = ((bluY*width)+bluX)*4;
+
+ outPixels[pixel] = refPixels[redPixel];
+ outPixels[pixel+1] = refPixels[grnPixel+1];
+ outPixels[pixel+2] = refPixels[bluPixel+2];
+ outPixels[pixel+3] = this._alphaMax ?
+ Math.max(refPixels[redPixel+3], refPixels[grnPixel+3], refPixels[bluPixel+3]) :
+ (refPixels[redPixel+3] + refPixels[grnPixel+3] + refPixels[bluPixel+3]) / 3;
+ }
+ }
+
+ return true;
+ };
+
+ createjs.AberrationFilter = createjs.promote(AberrationFilter, "Filter");
+}());
diff --git a/src/easeljs/filters/AlphaMapFilter.js b/src/easeljs/filters/AlphaMapFilter.js
index ee64231e8..70ccc285b 100644
--- a/src/easeljs/filters/AlphaMapFilter.js
+++ b/src/easeljs/filters/AlphaMapFilter.js
@@ -64,12 +64,16 @@ this.createjs = this.createjs || {};
* @class AlphaMapFilter
* @extends Filter
* @constructor
- * @param {HTMLImageElement|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
+ * @param {HTMLImageElement|HTMLCanvasElement|WebGLTexture} alphaMap The greyscale image (or canvas) to use as the alpha value for the
* result. This should be exactly the same dimensions as the target.
**/
function AlphaMapFilter(alphaMap) {
this.Filter_constructor();
-
+
+ if (!createjs.Filter.isValidImageSource(alphaMap)) {
+ throw "Must provide valid image source for alpha map, see Filter.isValidImageSource";
+ }
+
// public properties:
/**
* The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
@@ -79,35 +83,45 @@ this.createjs = this.createjs || {};
**/
this.alphaMap = alphaMap;
-
// private properties:
/**
- * @property _alphaMap
+ * @property _map
* @protected
* @type HTMLImageElement|HTMLCanvasElement
**/
- this._alphaMap = null;
+ this._map = null;
/**
- * @property _mapData
+ * @property _mapCtx
* @protected
- * @type Uint8ClampedArray
+ * @type CanvasRenderingContext2D
**/
- this._mapData = null;
+ this._mapCtx = null;
+
+ /**
+ * @property _mapTexture
+ * @protected
+ * @type WebGLTexture
+ */
this._mapTexture = null;
this.FRAG_SHADER_BODY = (
"uniform sampler2D uAlphaSampler;"+
"void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
+ "vec4 color = texture2D(uSampler, vTextureCoord);" +
"vec4 alphaMap = texture2D(uAlphaSampler, vTextureCoord);" +
// some image formats can have transparent white rgba(1,1,1, 0) when put on the GPU, this means we need a slight tweak
// using ceil ensure that the colour will be used so long as it exists but pure transparency will be treated black
- "gl_FragColor = vec4(color.rgb, color.a * (alphaMap.r * ceil(alphaMap.a)));" +
+ "float newAlpha = alphaMap.r * ceil(alphaMap.a);" +
+ "gl_FragColor = vec4(clamp(color.rgb/color.a, 0.0, 1.0) * newAlpha, newAlpha);" +
"}"
);
+
+ if(alphaMap instanceof WebGLTexture) {
+ this._mapTexture = alphaMap;
+ }
}
var p = createjs.extend(AlphaMapFilter, createjs.Filter);
@@ -116,12 +130,14 @@ this.createjs = this.createjs || {};
/** docced in super class **/
p.shaderParamSetup = function(gl, stage, shaderProgram) {
- if(!this._mapTexture) { this._mapTexture = gl.createTexture(); }
+ if(this._mapTexture === null) { this._mapTexture = gl.createTexture(); }
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
stage.setTextureParams(gl);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap);
+ if (this.alphaMap !== this._mapTexture) {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap);
+ }
gl.uniform1i(
gl.getUniformLocation(shaderProgram, "uAlphaSampler"),
@@ -133,8 +149,6 @@ this.createjs = this.createjs || {};
/** docced in super class **/
p.clone = function () {
var o = new AlphaMapFilter(this.alphaMap);
- o._alphaMap = this._alphaMap;
- o._mapData = this._mapData;
return o;
};
@@ -146,15 +160,43 @@ this.createjs = this.createjs || {};
// private methods:
/** docced in super class **/
- p._applyFilter = function (imageData) {
- if (!this.alphaMap) { return true; }
+ p._applyFilter = function(imageData) {
if (!this._prepAlphaMap()) { return false; }
-
- // TODO: update to support scenarios where the target has different dimensions.
- var data = imageData.data;
- var map = this._mapData;
- for(var i=0, l=data.length; i= 1) {
+ if (this._bufferTextureConcat === null) {
+ this._bufferTextureConcat = this._stageGL.getRenderBufferTexture(this._drawWidth, this._drawHeight);
+ } else {
+ this._stageGL.resizeTexture(this._bufferTextureConcat, this._drawWidth, this._drawHeight);
+ }
+ }
+ }
+
this._filterOffX = filterBounds.x;
this._filterOffY = filterBounds.y;
this.offX = this.x*this.scale + this._filterOffX;
@@ -355,24 +486,21 @@ this.createjs = this.createjs||{};
* @method release
**/
p.release = function() {
- if (this._webGLCache) {
- // if it isn't cache controlled clean up after yourself
- if (!this._webGLCache.isCacheControlled) {
- if (this.__lastRT){ this.__lastRT = undefined; }
- if (this.__rtA){ this._webGLCache._killTextureObject(this.__rtA); }
- if (this.__rtB){ this._webGLCache._killTextureObject(this.__rtB); }
- if (this.target && this.target.cacheCanvas){ this._webGLCache._killTextureObject(this.target.cacheCanvas); }
- }
+ if (this._stageGL) {
+ if (this._bufferTextureOutput !== null){ this._stageGL._killTextureObject(this._bufferTextureOutput); }
+ if (this._bufferTextureConcat !== null){ this._stageGL._killTextureObject(this._bufferTextureConcat); }
+ if (this._bufferTextureTemp !== null){ this._stageGL._killTextureObject(this._bufferTextureTemp); }
// set the context to none and let the garbage collector get the rest when the canvas itself gets removed
- this._webGLCache = false;
+ this._stageGL = false;
} else {
var stage = this.target.stage;
- if (stage instanceof createjs.StageGL){
- stage.releaseTexture(this.target.cacheCanvas);
+ if (stage instanceof createjs.StageGL) {
+ stage.releaseTexture(this._cacheCanvas);
}
}
- this.target = this.target.cacheCanvas = null;
+ this.disabled = true;
+ this.target = this._cacheCanvas = null;
this.cacheID = this._cacheDataURLID = this._cacheDataURL = undefined;
this.width = this.height = this.x = this.y = this.offX = this.offY = 0;
this.scale = 1;
@@ -386,11 +514,11 @@ this.createjs = this.createjs||{};
* @return {String} The image data url for the cache.
**/
p.getCacheDataURL = function() {
- var cacheCanvas = this.target && this.target.cacheCanvas;
+ var cacheCanvas = this.target && this._cacheCanvas;
if (!cacheCanvas) { return null; }
- if (this.cacheID != this._cacheDataURLID) {
+ if (this.cacheID !== this._cacheDataURLID) {
this._cacheDataURLID = this.cacheID;
- this._cacheDataURL = cacheCanvas.toDataURL?cacheCanvas.toDataURL():null; // incase function is
+ this._cacheDataURL = cacheCanvas.toDataURL ? cacheCanvas.toDataURL() : null;
}
return this._cacheDataURL;
};
@@ -402,8 +530,8 @@ this.createjs = this.createjs||{};
* @return {Boolean} Whether the draw was handled successfully.
**/
p.draw = function(ctx) {
- if(!this.target) { return false; }
- ctx.drawImage(this.target.cacheCanvas,
+ if (!this.target) { return false; }
+ ctx.drawImage(this._cacheCanvas,
this.x + (this._filterOffX/this.scale), this.y + (this._filterOffY/this.scale),
this._drawWidth/this.scale, this._drawHeight/this.scale
);
@@ -423,6 +551,20 @@ this.createjs = this.createjs||{};
);
};
+ /**
+ * Fetch the correct filter in order, complicated by multipass filtering.
+ * @param {Number} lookup The filter in the list to return
+ */
+ p._getGLFilter = function(lookup) {
+ if (this.target.filters === null || lookup < 0){ return undefined; }
+ var i = 0;
+ var result = this.target.filters[i];
+ while (result && --lookup >= 0) {
+ result = result._multiPass ? result._multiPass : this.target.filters[++i];
+ }
+ return result;
+ };
+
// private methods:
/**
* Create or resize the invisible canvas/surface that is needed for the display object(s) to draw to,
@@ -435,11 +577,12 @@ this.createjs = this.createjs||{};
var surface;
if (!this._options || !this._options.useGL) {
- surface = this.target.cacheCanvas;
+ surface = this._cacheCanvas;
// create it if it's missing
- if(!surface) {
- surface = this.target.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
+ if (!surface) {
+ surface = this._cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
+ this.disabled = this._disabled;
}
// now size it
@@ -449,51 +592,43 @@ this.createjs = this.createjs||{};
}
// create it if it's missing
- if (!this._webGLCache) {
+ if (!this._stageGL) {
if (this._options.useGL === "stage") {
- if(!(this.target.stage && this.target.stage.isWebGL)){
+ var targetStage = this.target.stage;
+ // use the stage that this object belongs on as the WebGL context
+ if (!(targetStage && targetStage.isWebGL)){
var error = "Cannot use 'stage' for cache because the object's parent stage is ";
- error += this.target.stage ? "non WebGL." : "not set, please addChild to the correct stage.";
+ error += targetStage ? "non WebGL." : "not set, please addChild to the correct stage.";
throw error;
}
- this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
- this._webGLCache = this.target.stage;
+ this._stageGL = targetStage;
- } else if(this._options.useGL === "new") {
- this.target.cacheCanvas = document.createElement("canvas"); // we can turn off autopurge because we wont be making textures here
- this._webGLCache = new createjs.StageGL(this.target.cacheCanvas, {antialias: true, transparent: true, autoPurge: -1});
- this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
+ } else if (this._options.useGL === "new") {
+ // create a new WebGL context to run this cache
+ this._cacheCanvas = document.createElement("canvas"); // low autopurge in case of filter swapping and low texture count
+ this._stageGL = new createjs.StageGL(this._cacheCanvas, {antialias: true, transparent: true, autoPurge: 10});
+ if (!this._stageGL._webGLContext){ throw "GL Cache asked for but unavailable"; }
+ this._stageGL.isCacheControlled = true; // use this flag to control stage sizing and final output
- } else if(this._options.useGL instanceof createjs.StageGL) {
- this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
- this._webGLCache = this._options.useGL;
- this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
+ } else if (this._options.useGL instanceof createjs.StageGL) {
+ // use the provided WebGL context to run this cache, trust the user it works and is configured.
+ this._stageGL = this._options.useGL;
} else {
throw "Invalid option provided to useGL, expected ['stage', 'new', StageGL, undefined], got "+ this._options.useGL;
}
}
- // now size render surfaces
- surface = this.target.cacheCanvas;
- var stageGL = this._webGLCache;
+ this.disabled = this._disabled;
- // if we have a dedicated stage we've gotta size it
+ // if we have a dedicated stage we've got to size it
+ var stageGL = this._stageGL;
if (stageGL.isCacheControlled) {
+ surface = this._cacheCanvas;
surface.width = this._drawWidth;
surface.height = this._drawHeight;
stageGL.updateViewport(this._drawWidth, this._drawHeight);
}
- if (this.target.filters) {
- // with filters we can't tell how many we'll need but the most we'll ever need is two, so make them now
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- } else {
- // without filters then we only need one RenderTexture, and that's only if its not a dedicated stage
- if (!stageGL.isCacheControlled) {
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- }
- }
};
/**
@@ -502,18 +637,12 @@ this.createjs = this.createjs||{};
* @protected
**/
p._drawToCache = function(compositeOperation) {
- var surface = this.target.cacheCanvas;
+ var surface = this._cacheCanvas;
var target = this.target;
- var webGL = this._webGLCache;
-
- if (webGL){
- webGL.cacheDraw(target, target.filters, this);
-
- // we may of swapped around which element the surface is, so we re-fetch it
- surface = this.target.cacheCanvas;
+ var webGL = this._stageGL;
- surface.width = this._drawWidth;
- surface.height = this._drawHeight;
+ if (webGL) {
+ webGL.cacheDraw(target, this);
} else {
var ctx = surface.getContext("2d");
@@ -528,7 +657,6 @@ this.createjs = this.createjs||{};
target.draw(ctx, true);
ctx.restore();
-
if (target.filters && target.filters.length) {
this._applyFilters(ctx);
}
@@ -551,14 +679,14 @@ this.createjs = this.createjs||{};
var i = 0, filter = filters[i];
do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
- if(filter.usesContext){
- if(data) {
+ if (filter.usesContext){
+ if (data) {
ctx.putImageData(data, 0,0);
data = null;
}
filter.applyFilter(ctx, 0,0, w,h);
} else {
- if(!data) {
+ if (!data) {
data = ctx.getImageData(0,0, w,h);
}
filter._applyFilter(data);
@@ -569,7 +697,7 @@ this.createjs = this.createjs||{};
} while (filter);
//done
- if(data) {
+ if (data) {
ctx.putImageData(data, 0,0);
}
};
diff --git a/src/easeljs/filters/BlurFilter.js b/src/easeljs/filters/BlurFilter.js
index cc888925f..20bad1404 100644
--- a/src/easeljs/filters/BlurFilter.js
+++ b/src/easeljs/filters/BlurFilter.js
@@ -115,7 +115,7 @@ this.createjs = this.createjs||{};
"for(int i=0; i<{{blurX}}; i++) {" +
"for(int j=0; j<{{blurY}}; j++) {" +
- "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" +
+ "sampleOffset = vTextureCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" +
"color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" +
"}" +
"}" +
@@ -163,7 +163,7 @@ this.createjs = this.createjs||{};
}
return this._compiledShader;
};
- p._setShader = function() { this._compiledShader; };
+ p._setShader = function(value) { this._compiledShader = value; };
try {
Object.defineProperties(p, {
diff --git a/src/easeljs/filters/ColorFilter.js b/src/easeljs/filters/ColorFilter.js
index 4fcfa491c..20a7f77ff 100644
--- a/src/easeljs/filters/ColorFilter.js
+++ b/src/easeljs/filters/ColorFilter.js
@@ -135,7 +135,7 @@ this.createjs = this.createjs||{};
"uniform vec4 uColorOffset;" +
"void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
+ "vec4 color = texture2D(uSampler, vTextureCoord);" +
"gl_FragColor = (color * uColorMultiplier) + uColorOffset;" +
"}"
diff --git a/src/easeljs/filters/ColorMatrix.js b/src/easeljs/filters/ColorMatrix.js
index b6279172e..053325c52 100644
--- a/src/easeljs/filters/ColorMatrix.js
+++ b/src/easeljs/filters/ColorMatrix.js
@@ -107,6 +107,52 @@ this.createjs = this.createjs||{};
ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length;
+// static methods:
+ /**
+ * Create an instance of ColorMatrix using the Sepia preset
+ * @returns {ColorMatrix}
+ */
+ ColorMatrix.createSepiaPreset = function() {
+ return (new ColorMatrix()).copy([
+ 0.4977, 0.9828, 0.1322, 0.0000, 14,
+ 0.4977, 0.9828, 0.1322, 0.0000, -14,
+ 0.4977, 0.9828, 0.1322, 0.0000, -47,
+ 0.0000, 0.0000, 0.0000, 1.0000, 0,
+ 0, 0, 0, 0, 1
+ ]);
+ };
+
+ /**
+ * Create an instance of ColorMatrix using an invert color preset
+ * @returns {ColorMatrix}
+ */
+ ColorMatrix.createInvertPreset = function() {
+ return (new ColorMatrix()).copy([
+ -1.0000, 0.0000, 0.0000, 0.0000, 255,
+ 0.0000, -1.0000, 0.0000, 0.0000, 255,
+ 0.0000, 0.0000, -1.0000, 0.0000, 255,
+ 0.0000, 0.0000, 0.0000, 1.0000, 0,
+ 0, 0, 0, 0, 1
+ ]);
+ };
+
+ /**
+ * Create an instance of ColorMatrix using the Greyscale preset.
+ * Note: -100 saturation accounts for perceived brightness, the greyscale preset treats all channels equally.
+ * @returns {ColorMatrix}
+ */
+ ColorMatrix.createGreyscalePreset = function() {
+ return (new ColorMatrix()).copy([
+ 0.3333, 0.3334, 0.3333, 0.0000, 0,
+ 0.3333, 0.3334, 0.3333, 0.0000, 0,
+ 0.3333, 0.3334, 0.3333, 0.0000, 0,
+ 0.0000, 0.0000, 0.0000, 1.0000, 0,
+ 0, 0, 0, 0, 1
+ ]);
+ };
+
+ //TODO: some "fun" filters
+
// public methods:
/**
* Resets the instance with the specified values.
@@ -172,6 +218,24 @@ this.createjs = this.createjs||{};
return this;
};
+ /**
+ * Adjusts the colour offset of pixel color by adding the specified value to the red, green and blue channels.
+ * Positive values will make the image brighter, negative values will make it darker.
+ * @method adjustBrightness
+ * @param {Number} r A value between -255 & 255 that will be added to the Red channel.
+ * @param {Number} g A value between -255 & 255 that will be added to the Green channel.
+ * @param {Number} b A value between -255 & 255 that will be added to the Blue channel.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.adjustOffset = function(r,g,b) {
+ if (isNaN(r) || isNaN(g) || isNaN(b)) { return this; }
+ this[4] = this._cleanValue(this[4] + r,255);
+ this[9] = this._cleanValue(this[9] + g,255);
+ this[14] = this._cleanValue(this[14] + b,255);
+ return this;
+ };
+
/**
* Adjusts the contrast of pixel color.
* Positive values will increase contrast, negative values will decrease contrast.
@@ -313,7 +377,13 @@ this.createjs = this.createjs||{};
* @return {String} a string representation of the instance.
**/
p.toString = function() {
- return "[ColorMatrix]";
+ var sz = "";
+ sz += " "+ this[0].toFixed(4)+", "+this[1].toFixed(4)+", "+this[2].toFixed(4)+", "+this[3].toFixed(4)+", "+(this[4]|0)+",\n";
+ sz += " "+ this[5].toFixed(4)+", "+this[6].toFixed(4)+", "+this[7].toFixed(4)+", "+this[8].toFixed(4)+", "+(this[9]|0)+",\n";
+ sz += " "+ this[10].toFixed(4)+", "+this[11].toFixed(4)+", "+this[12].toFixed(4)+", "+this[13].toFixed(4)+", "+(this[14]|0)+",\n";
+ sz += " "+ this[15].toFixed(4)+", "+this[16].toFixed(4)+", "+this[17].toFixed(4)+", "+this[18].toFixed(4)+", "+(this[19]|0)+",\n";
+ sz += " "+ (this[20]|0)+", "+(this[21]|0)+", "+(this[22]|0)+", "+(this[23]|0)+", "+(this[24]|0)+"\n";
+ return "[ColorMatrix] {\n"+ sz +"}";
};
@@ -367,6 +437,5 @@ this.createjs = this.createjs||{};
return matrix;
};
-
createjs.ColorMatrix = ColorMatrix;
}());
diff --git a/src/easeljs/filters/ColorMatrixFilter.js b/src/easeljs/filters/ColorMatrixFilter.js
index 6d189f58f..d88ce3cbd 100644
--- a/src/easeljs/filters/ColorMatrixFilter.js
+++ b/src/easeljs/filters/ColorMatrixFilter.js
@@ -56,6 +56,18 @@ this.createjs = this.createjs||{};
*
* shape.cache(-50, -50, 100, 100);
*
+ * Example
+ * This example uses a preset to generate a sepia photograph effect
+ *
+ * var shape = new createjs.Shape().set({x:100,y:100});
+ * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
+ *
+ * shape.filters = [
+ * new createjs.ColorMatrixFilter( createjs.ColorMatrix.createSepiaPreset() )
+ * ];
+ *
+ * shape.cache(-50, -50, 100, 100);
+ *
* See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
* @class ColorMatrixFilter
* @constructor
@@ -79,14 +91,15 @@ this.createjs = this.createjs||{};
"uniform vec4 uColorMatrixOffset;" +
"void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
+ "vec4 color = texture2D(uSampler, vTextureCoord);" +
"mat4 m = uColorMatrix;" +
- "vec4 newColor = vec4(0,0,0,0);" +
- "newColor.r = color.r*m[0][0] + color.g*m[0][1] + color.b*m[0][2] + color.a*m[0][3];" +
- "newColor.g = color.r*m[1][0] + color.g*m[1][1] + color.b*m[1][2] + color.a*m[1][3];" +
- "newColor.b = color.r*m[2][0] + color.g*m[2][1] + color.b*m[2][2] + color.a*m[2][3];" +
- "newColor.a = color.r*m[3][0] + color.g*m[3][1] + color.b*m[3][2] + color.a*m[3][3];" +
+ "vec4 newColor = vec4(" +
+ "color.r*m[0][0] + color.g*m[0][1] + color.b*m[0][2] + color.a*m[0][3]," +
+ "color.r*m[1][0] + color.g*m[1][1] + color.b*m[1][2] + color.a*m[1][3]," +
+ "color.r*m[2][0] + color.g*m[2][1] + color.b*m[2][2] + color.a*m[2][3]," +
+ "color.r*m[3][0] + color.g*m[3][1] + color.b*m[3][2] + color.a*m[3][3]" +
+ ");" +
"gl_FragColor = newColor + uColorMatrixOffset;" +
"}"
diff --git a/src/easeljs/filters/DisplacementFilter.js b/src/easeljs/filters/DisplacementFilter.js
new file mode 100644
index 000000000..e07f057bb
--- /dev/null
+++ b/src/easeljs/filters/DisplacementFilter.js
@@ -0,0 +1,211 @@
+/*
+ * DisplacementFilter
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ * Copyright (c) 2010 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module EaselJS
+ */
+
+// namespace:
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Distorts portions of the image, creates effects like computer glitches, bulge/pinch, and pond ripples. This filter
+ * Uses a reference image/canvas and interprets its r/g channels as a displacement map. A displacement map species
+ * how far from the original pixel the output pixel should be taken from.
+ *
+ * Painting a displacement map means understanding how the r/g channel are interpreted. The red channel changes
+ * how far in the x to sample, and the green changes how far in the y to sample. The maximum range is -distance to
+ * +distance ('distance' being the distance value on the filter instance). This maps to the 0-255 color space for
+ * each channel. This means that for an image to experience no change it should be painted #808000 as that is in the
+ * middle for both the x and the y displacements.
+ *
+ * - Red values smaller than 0x80 sample from further left, and larger than 0x80 sample form further right.
+ * - Green values smaller than 0x80 sample from higher up, and larger than 0x80 sample form lower down.
+ *
+ * The output vs input can take some getting used to so experiment! As a tip: when painting to a canvas that
+ * will act as displacement map, consider using the blend mode of "overlay" or "softlight". These blend modes
+ * will preserve mid tones (no displacement) while still mixing the high and low values as you might need per channel.
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
+ * @class DisplacementFilter
+ * @extends Filter
+ * @constructor
+ * @param {HTMLImageElement|HTMLCanvasElement|WebGLTexture} dudvMap The horizontal blur radius in pixels.
+ * @param {Number} [distance=0] The absolute value of the maximum possible displacement from the original pixel.
+ **/
+ function DisplacementFilter(dudvMap, distance) {
+ this.Filter_constructor();
+
+ if (!createjs.Filter.isValidImageSource(dudvMap)) {
+ throw "Must provide valid image source for displacement map, see Filter.isValidImageSource";
+ }
+
+ // public properties:
+ /**
+ * The visual source to fetch the displacement map from.
+ * @property dudvMap
+ * @type {Image|HTMLCanvasElement}
+ **/
+ this.dudvMap = dudvMap;
+
+ /**
+ * The absolute value of the maximum shift in x/y possible.
+ * @property distance
+ * @default 128
+ * @type {Image|HTMLCanvasElement}
+ **/
+ this.distance = Number(distance);
+ if(isNaN(this.distance)) { this.distance = 128; }
+
+ /**
+ * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}}
+ */
+ this.FRAG_SHADER_BODY = (
+ "uniform sampler2D uDudvSampler;"+
+ "uniform float fPower;" +
+ "uniform vec2 pixelAdjustment;" +
+
+ "void main(void) {" +
+ "vec4 dudvValue = texture2D(uDudvSampler, vTextureCoord);" +
+ "vec2 sampleOffset = mix(vec2(0.0), dudvValue.rg-0.5, dudvValue.a) * (fPower*pixelAdjustment);" +
+ "gl_FragColor = texture2D(uSampler, vTextureCoord + sampleOffset);" +
+ "}"
+ );
+
+ if(dudvMap instanceof WebGLTexture) {
+ this._mapTexture = dudvMap;
+ } else if (dudvMap instanceof HTMLCanvasElement) {
+ this._dudvCanvas = dudvMap;
+ this._dudvCtx = dudvMap.getContext("2d");
+ } else {
+ var canvas = this._dudvCanvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
+ canvas.width = dudvMap.width;
+ canvas.height = dudvMap.height;
+ (this._dudvCtx = canvas.getContext("2d")).drawImage(dudvMap, 0,0);
+ }
+ }
+ var p = createjs.extend(DisplacementFilter, createjs.Filter);
+
+
+// public methods:
+ /** docced in super class **/
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {
+ if (!this._mapTexture) { this._mapTexture = gl.createTexture(); }
+
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
+ stage.setTextureParams(gl);
+ if (this.dudvMap !== this._mapTexture) {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.dudvMap);
+ }
+
+ gl.uniform1i(
+ gl.getUniformLocation(shaderProgram, "uDudvSampler"),
+ 1
+ );
+
+ gl.uniform1f(
+ gl.getUniformLocation(shaderProgram, "fPower"),
+ this.distance
+ );
+
+ gl.uniform2f( //this is correct as the color maps to -0.5,0.5. This compounds the pixel delta, thus 2/size
+ gl.getUniformLocation(shaderProgram, "pixelAdjustment"),
+ 2/stage._viewportWidth, -2/stage._viewportHeight
+ );
+ };
+
+// private methods:
+ /** docced in super class **/
+ p._applyFilter = function(imageData) {
+ // as we're reaching across pixels we need an unmodified clone of the source
+ // slice/from/map/filter don't work correctly in IE11 and subarray creates a ref
+ var refArray, refArraySrc = imageData.data;
+ if (refArraySrc.slice !== undefined) {
+ refArray = refArraySrc.slice();
+ } else {
+ refArray = new Uint8ClampedArray(refArraySrc.length);
+ refArray.set(refArraySrc);
+ }
+
+ var outArray = imageData.data;
+ var width = imageData.width;
+ var height = imageData.height;
+ var rowOffset, pixelStart;
+
+ var sampleData = this._dudvCtx.getImageData(0,0, this.dudvMap.width,this.dudvMap.height);
+ var sampleArray = sampleData.data;
+ var sampleWidth = sampleData.width;
+ var sampleHeight = sampleData.height;
+ var sampleRowOffset, samplePixelStart;
+
+ var widthRatio = sampleWidth/width;
+ var heightRatio = sampleHeight/height;
+ var pxRange = 1/255;
+
+ // performance optimizing lookup
+ var distance = this.distance*2;
+
+ // the x and y need to stretch separately, nesting the for loops simplifies this logic even if the array is flat
+ for (var i=0; i width) { xDelta = width-j; }
+ if (i+yDelta < 0) { yDelta = -i; }
+ if (i+yDelta > height) { yDelta = height-i; }
+
+ var targetPixelStart = (pixelStart + xDelta*4) + yDelta*4*width;
+ outArray[pixelStart] = refArray[targetPixelStart];
+ outArray[pixelStart+1] = refArray[targetPixelStart+1];
+ outArray[pixelStart+2] = refArray[targetPixelStart+2];
+ outArray[pixelStart+3] = refArray[targetPixelStart+3];
+ }
+ }
+
+ return true;
+ };
+
+ createjs.DisplacementFilter = createjs.promote(DisplacementFilter, "Filter");
+}());
diff --git a/src/easeljs/filters/Filter.js b/src/easeljs/filters/Filter.js
index 045993f8f..f4a3ea125 100644
--- a/src/easeljs/filters/Filter.js
+++ b/src/easeljs/filters/Filter.js
@@ -39,9 +39,11 @@ this.createjs = this.createjs||{};
// constructor:
/**
- * Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
- * the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
- * {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
+ * Base class that all filters should inherit from. Appli
+ *
+ * When on a regular Stage apply the Filters and then cache the object using the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method.
+ * When a cached object changes, please use {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}.
+ * When on a StageGL simply setting content in the `.filters` array will trigger an automatic and constantly updated cache.
*
* Example
*
@@ -55,13 +57,20 @@ this.createjs = this.createjs||{};
* margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
* will cause an object to feather outwards, resulting in a margin around the shape.
*
+ * Any filter that consumes an external image stretches the image to cover the cached bounds. If this is an undesired
+ * visual result, then use an intermediary cache to properly size and layout your data before passing it to a filter.
+ *
+ *
* EaselJS Filters
* EaselJS comes with a number of pre-built filters:
- * - {{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object
- * - {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object
- * - {{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object
- * - {{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object
- * - {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}
+ *
+ * - {{#crossLink "AberrationFilter"}}{{/crossLink}} : Shift the RGB components separately along a given vector
+ * - {{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object
+ * - {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object
+ * - {{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object
+ * - {{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object
+ * - {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}
+ * - {{#crossLink "DisplacementFilter"}}{{/crossLink}}: Create localized distortions in supplied display object
*
*
* @class Filter
@@ -89,7 +98,6 @@ this.createjs = this.createjs||{};
* Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
* This should be based upon StageGL.SHADER_VERTEX_BODY_REGULAR
* @property VTX_SHADER
- * @virtual
* @type {String}
* @readonly
*/
@@ -99,7 +107,6 @@ this.createjs = this.createjs||{};
* Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
* This should be based upon StageGL.SHADER_FRAGMENT_BODY_REGULAR
* @property FRAG_SHADER
- * @virtual
* @type {String}
* @readonly
*/
@@ -107,6 +114,30 @@ this.createjs = this.createjs||{};
}
var p = Filter.prototype;
+// static methods:
+ /**
+ * Check to see if an image source being provided is one that is valid.
+ * Valid Sources:
+ *
+ * - Image Object
+ * - HTML Canvas Element
+ * - `.cacheCanvas` on an object with the same stage
+ *
+ * WebGLTextures CANNOT be shared between multiple WebGL contexts. This means the only safe source for a WebGLTexture
+ * is an object cached using the same StageGL as the object trying to use it in a filter. This function does not
+ * enforce that restriction, as it is difficult or expensive to detect. The render will crash or fail to load the
+ * image data if the rule isn't followed.
+ * @param {HTMLImageElement|HTMLCanvasElement|WebGLTexture} src The element to check for validity
+ * @return Boolean Whether the source is valid
+ */
+ Filter.isValidImageSource = function(src) {
+ return Boolean(src) && (
+ src instanceof Image ||
+ src instanceof WebGLTexture ||
+ src instanceof HTMLCanvasElement
+ );
+ };
+
// public methods:
/**
* Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to.
@@ -136,23 +167,19 @@ this.createjs = this.createjs||{};
* @param {Number} y The y position to use for the source rect.
* @param {Number} width The width to use for the source rect.
* @param {Number} height The height to use for the source rect.
- * @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
- * @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
- * @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
+ * @param {CanvasRenderingContext2D} [targetCtx=ctx] The 2D context to draw the result to. Defaults to the context passed to ctx.
* @return {Boolean} If the filter was applied successfully.
**/
- p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
+ p.applyFilter = function(ctx, x, y, width, height, targetCtx) {
// this is the default behaviour because most filters access pixel data. It is overridden when not needed.
targetCtx = targetCtx || ctx;
- if (targetX == null) { targetX = x; }
- if (targetY == null) { targetY = y; }
try {
var imageData = ctx.getImageData(x, y, width, height);
} catch (e) {
return false;
}
if (this._applyFilter(imageData)) {
- targetCtx.putImageData(imageData, targetX, targetY);
+ targetCtx.putImageData(imageData, x, y);
return true;
}
return false;
diff --git a/src/easeljs/utils/WebGLInspector.js b/src/easeljs/utils/WebGLInspector.js
index 60c42e85c..09ae74c2f 100644
--- a/src/easeljs/utils/WebGLInspector.js
+++ b/src/easeljs/utils/WebGLInspector.js
@@ -43,26 +43,10 @@ this.createjs = this.createjs||{};
* @constructor
* @param {StageGL} stage The default stage to use when none is supplied.
*/
- function WebGLInspector(stage) {
- this.EventDispatcher_constructor();
-
- // public properties:
-
- // private properties:
- /**
- * The internal reference to the default stage this Inspector is for.
- * @property _stage
- * @protected
- * @type {StageGL}
- */
- this._stage = stage;
-
- // and begin
- this._initializeWebGLInspector();
- }
+ function WebGLInspector(stage) {}
var p = createjs.extend(WebGLInspector, createjs.EventDispatcher);
-// static:
+// properties:
/**
* Alternate output for debugging situations where "console" is not available, i.e. Mobile or remote debugging.
* Expects object with a "log" function that takes any number of params.
@@ -72,84 +56,101 @@ this.createjs = this.createjs||{};
* @static
* @protected
*/
- p.alternateOutput = undefined;
+ WebGLInspector.alternateOutput = undefined;
-// getter / setters:
-
-// ctor:
/**
- * @method _initializeWebGL
- * @protected
+ * Default stage to assume when non provided
+ * @type {StageGL}
+ * @private
*/
- p._initializeWebGLInspector = function() {};
+ WebGLInspector.stage = undefined;
// public methods:
+ /**
+ * Utility to call the right logging
+ * @params *
+ */
+ WebGLInspector.log = function() {
+ (WebGLInspector.alternateOutput ? WebGLInspector.alternateOutput.log : console.log).apply(this, arguments);
+ };
+
/**
* Perform all of the logging reports at once.
* @method log
- * @param {StageGL} [stage=this._stage] The stage to log information for.
+ * @param {StageGL} [stage=WebGLInspector.stage] The stage to log information for.
*/
- p.log = function(stage) {
- if(!stage){ stage = this._stage; }
+ WebGLInspector.logAll = function(stage) {
+ if (!stage){ stage = WebGLInspector.stage; }
- console.log("Batches Per Draw", (stage._batchID/stage._drawID).toFixed(4));
- this.logContextInfo(stage._webGLContext);
- this.logDepth(stage.children, "");
- this.logTextureFill(stage);
+ WebGLInspector.log("Batches Per Draw", (stage._batchID/stage._drawID).toFixed(4));
+ WebGLInspector.logContextInfo(stage._webGLContext);
+ WebGLInspector.logDepth(stage.children, "");
+ WebGLInspector.logTextureFill(stage);
};
/**
- * Replace the stage's Draw command with an empty draw command. This is useful for testing performance, and ignoring
- * rendering.
- * @method toggleGPUDraw
- * @param {StageGL} [stage=this._stage] The stage to log information for.
- * @param {Boolean} enabled Force enabled. If left undefined, it will toggle.
+ * Replace the stage's Draw command with a new draw command. This is useful for:
+ *
+ * - Testing performance, with no render cost. See `WebGLInspector.drawEmpty`
+ * - Troubleshooting and tracking loaded textures. See `WebGLInspector.drawTexOnBuffer`
+ * - Misc feature or troubleshooting injection
+ *
+ * @method replaceRenderBatchCall
+ * @param {StageGL} [stage=WebGLInspector.stage] The stage to log information for.
+ * @param {Function} newFunc .
*/
- p.toggleGPUDraw = function(stage, enabled) {
- if(!stage){ stage = this._stage; }
+ WebGLInspector.replaceRenderBatchCall = function(stage, newFunc) {
+ if (!stage){ stage = WebGLInspector.stage; }
- if(enabled === undefined) {
- enabled = !!stage._drawBuffers_;
+ if (newFunc === undefined && stage._renderBatch_) {
+ stage._renderBatch = stage._renderBatch_;
+ stage._renderBatch_ = undefined;
+ } else {
+ if (stage._renderBatch_ === undefined) {
+ stage._renderBatch_ = stage._renderBatch;
+ }
+ stage._renderBatch = newFunc;
}
+ };
- if(enabled) {
- if(stage._drawBuffers_) {
- stage._drawBuffers = stage._drawBuffers_;
- stage._drawBuffers_ = undefined;
- }
+ /**
+ * Identical to replaceRenderBatchCall, but affects the Cover command.
+ * @method replaceRenderCoverCall
+ * @param {StageGL} [stage=WebGLInspector.stage] The stage to log information for.
+ * @param {Function} newFunc .
+ */
+ WebGLInspector.replaceRenderCoverCall = function(stage, newFunc) {
+ if (!stage){ stage = WebGLInspector.stage; }
+
+ if (newFunc === undefined && stage._renderCover_) {
+ stage._renderCover = stage._renderCover_;
+ stage._renderCover_ = undefined;
} else {
- stage._drawBuffers_ = stage._drawBuffers;
- stage._drawBuffers = function(gl) {
- if(this.vocalDebug) {
- var output = "BlankDraw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason;
- this.alternateOutput?this.alternateOutput.log(output):console.log(output);
- }
- };
+ if (stage._renderCover_ === undefined) {
+ stage._renderCover_ = stage._renderCover;
+ }
+ stage._renderCover = newFunc;
}
};
/**
* Recursively walk the entire display tree, log the attached items, and display it in a tree view.
* @method logDepth
- * @param {Array} [children=this._stage.children] The children array to walk through.
+ * @param {Array} [children=WebGLInspector.stage.children] The children array to walk through.
* @param {String} prepend What to prepend to this output from this point onwards.
* @param {Function} customLog Which logging function to use, mainly for filtering or formatting output.
* Fallback hierarchy is customLog -> alternateOutput -> console.log.
*/
- p.logDepth = function(children, prepend, customLog) {
- if(!children){ children = this._stage.children; }
- if(!prepend){ prepend = ""; }
+ WebGLInspector.logDepth = function(children, prepend, customLog) {
+ if (!children){ children = WebGLInspector.stage.children; }
+ if (!prepend){ prepend = ""; }
var l = children.length;
- for(var i=0; i