From bb3e231feca3311508d23d64a8c20e1605291ba0 Mon Sep 17 00:00:00 2001 From: Gousalya-hmh <54981888+Gousalya-hmh@users.noreply.github.com> Date: Fri, 27 May 2022 12:32:18 +0530 Subject: [PATCH 1/2] Altered touch fix --- lib/easeljs.js | 31767 ++++++++++++++++++++++++----------------------- 1 file changed, 15891 insertions(+), 15876 deletions(-) diff --git a/lib/easeljs.js b/lib/easeljs.js index d28e13489..917b11b41 100644 --- a/lib/easeljs.js +++ b/lib/easeljs.js @@ -1,466 +1,466 @@ -/*! -* EaselJS -* 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. -*/ - - -//############################################################################## -// extend.js -//############################################################################## - -this.createjs = this.createjs||{}; - -/** - * @class Utility Methods - */ - -/** - * Sets up the prototype chain and constructor property for a new class. - * - * This should be called right after creating the class constructor. - * - * function MySubClass() {} - * createjs.extend(MySubClass, MySuperClass); - * MySubClass.prototype.doSomething = function() { } - * - * var foo = new MySubClass(); - * console.log(foo instanceof MySuperClass); // true - * console.log(foo.prototype.constructor === MySubClass); // true - * - * @method extend - * @param {Function} subclass The subclass. - * @param {Function} superclass The superclass to extend. - * @return {Function} Returns the subclass's new prototype. - */ -createjs.extend = function(subclass, superclass) { - "use strict"; - - function o() { this.constructor = subclass; } - o.prototype = superclass.prototype; - return (subclass.prototype = new o()); -}; - -//############################################################################## -// promote.js -//############################################################################## - -this.createjs = this.createjs||{}; - -/** - * @class Utility Methods - */ - -/** - * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`. - * It is recommended to use the super class's name as the prefix. - * An alias to the super class's constructor is always added in the format `prefix_constructor`. - * This allows the subclass to call super class methods without using `function.call`, providing better performance. - * - * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")` - * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the - * prototype of `MySubClass` as `MySuperClass_draw`. - * - * This should be called after the class's prototype is fully defined. - * - * function ClassA(name) { - * this.name = name; - * } - * ClassA.prototype.greet = function() { - * return "Hello "+this.name; - * } - * - * function ClassB(name, punctuation) { - * this.ClassA_constructor(name); - * this.punctuation = punctuation; - * } - * createjs.extend(ClassB, ClassA); - * ClassB.prototype.greet = function() { - * return this.ClassA_greet()+this.punctuation; - * } - * createjs.promote(ClassB, "ClassA"); - * - * var foo = new ClassB("World", "!?!"); - * console.log(foo.greet()); // Hello World!?! - * - * @method promote - * @param {Function} subclass The class to promote super class methods on. - * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass. - * @return {Function} Returns the subclass. - */ -createjs.promote = function(subclass, prefix) { - "use strict"; - - var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__; - if (supP) { - subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable - for (var n in supP) { - if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; } - } - } - return subclass; -}; - -//############################################################################## -// indexOf.js -//############################################################################## - -this.createjs = this.createjs||{}; - -/** - * @class Utility Methods - */ - -/** - * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of - * that value. Returns -1 if value is not found. - * - * var i = createjs.indexOf(myArray, myElementToFind); - * - * @method indexOf - * @param {Array} array Array to search for searchElement - * @param searchElement Element to find in array. - * @return {Number} The first index of searchElement in array. - */ -createjs.indexOf = function (array, searchElement){ - "use strict"; - - for (var i = 0,l=array.length; i < l; i++) { - if (searchElement === array[i]) { - return i; - } - } - return -1; -}; - -//############################################################################## -// UID.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. UID.get()) - * and should not be instantiated. - * @class UID - * @static - **/ - function UID() { - throw "UID cannot be instantiated"; - } - - -// private static properties: - /** - * @property _nextID - * @type Number - * @protected - **/ - UID._nextID = 0; - - -// public static methods: - /** - * Returns the next unique id. - * @method get - * @return {Number} The next unique id - * @static - **/ - UID.get = function() { - return UID._nextID++; - }; - - - createjs.UID = UID; -}()); - -//############################################################################## -// deprecate.js -//############################################################################## - -this.createjs = this.createjs||{}; - -/** - * @class Utility Methods - */ - -/** - * Wraps deprecated methods so they still be used, but throw warnings to developers. - * - * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod); - * - * The recommended approach for deprecated properties is: - * - * try { - * Obj ect.defineProperties(object, { - * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) }, - * readWriteProp: { - * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }), - * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; }) - * }); - * } catch (e) {} - * - * @method deprecate - * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how - * @param {String} [name=null] The name of the method or property to display in the console warning. - * to deprecate properties. - * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after - * logging the warning in the console. - */ -createjs.deprecate = function(fallbackMethod, name) { - "use strict"; - return function() { - var msg = "Deprecated property or method '"+name+"'. See docs for info."; - console && (console.warn ? console.warn(msg) : console.log(msg)); - return fallbackMethod && fallbackMethod.apply(this, arguments); - } -}; - -//############################################################################## -// Event.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - -// constructor: - /** - * Contains properties and methods shared by all events for use with - * {{#crossLink "EventDispatcher"}}{{/crossLink}}. - * - * Note that Event objects are often reused, so you should never - * rely on an event object's state outside of the call stack it was received in. - * @class Event - * @param {String} type The event type. - * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. - * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. - * @constructor - **/ - function Event(type, bubbles, cancelable) { - - - // public properties: - /** - * The type of event. - * @property type - * @type String - **/ - this.type = type; - - /** - * The object that generated an event. - * @property target - * @type Object - * @default null - * @readonly - */ - this.target = null; - - /** - * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will - * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event - * is generated from childObj, then a listener on parentObj would receive the event with - * target=childObj (the original target) and currentTarget=parentObj (where the listener was added). - * @property currentTarget - * @type Object - * @default null - * @readonly - */ - this.currentTarget = null; - - /** - * For bubbling events, this indicates the current event phase:
    - *
  1. capture phase: starting from the top parent to the target
  2. - *
  3. at target phase: currently being dispatched from the target
  4. - *
  5. bubbling phase: from the target to the top parent
  6. - *
- * @property eventPhase - * @type Number - * @default 0 - * @readonly - */ - this.eventPhase = 0; - - /** - * Indicates whether the event will bubble through the display list. - * @property bubbles - * @type Boolean - * @default false - * @readonly - */ - this.bubbles = !!bubbles; - - /** - * Indicates whether the default behaviour of this event can be cancelled via - * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor. - * @property cancelable - * @type Boolean - * @default false - * @readonly - */ - this.cancelable = !!cancelable; - - /** - * The epoch time at which this event was created. - * @property timeStamp - * @type Number - * @default 0 - * @readonly - */ - this.timeStamp = (new Date()).getTime(); - - /** - * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called - * on this event. - * @property defaultPrevented - * @type Boolean - * @default false - * @readonly - */ - this.defaultPrevented = false; - - /** - * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or - * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event. - * @property propagationStopped - * @type Boolean - * @default false - * @readonly - */ - this.propagationStopped = false; - - /** - * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called - * on this event. - * @property immediatePropagationStopped - * @type Boolean - * @default false - * @readonly - */ - this.immediatePropagationStopped = false; - - /** - * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event. - * @property removed - * @type Boolean - * @default false - * @readonly - */ - this.removed = false; - } - var p = Event.prototype; - -// public methods: - /** - * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable. - * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will - * cancel the default behaviour associated with the event. - * @method preventDefault - **/ - p.preventDefault = function() { - this.defaultPrevented = this.cancelable&&true; - }; - - /** - * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true. - * Mirrors the DOM event standard. - * @method stopPropagation - **/ - p.stopPropagation = function() { - this.propagationStopped = true; - }; - - /** - * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and - * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true. - * Mirrors the DOM event standard. - * @method stopImmediatePropagation - **/ - p.stopImmediatePropagation = function() { - this.immediatePropagationStopped = this.propagationStopped = true; - }; - - /** - * Causes the active listener to be removed via removeEventListener(); - * - * myBtn.addEventListener("click", function(evt) { - * // do stuff... - * evt.remove(); // removes this listener. - * }); - * - * @method remove - **/ - p.remove = function() { - this.removed = true; - }; - - /** - * Returns a clone of the Event instance. - * @method clone - * @return {Event} a clone of the Event instance. - **/ - p.clone = function() { - return new Event(this.type, this.bubbles, this.cancelable); - }; - - /** - * Provides a chainable shortcut method for setting a number of properties on the instance. - * - * @method set - * @param {Object} props A generic object containing properties to copy to the instance. - * @return {Event} Returns the instance the method is called on (useful for chaining calls.) - * @chainable - */ - p.set = function(props) { - for (var n in props) { this[n] = props[n]; } - return this; - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Event (type="+this.type+")]"; - }; - - createjs.Event = Event; -}()); - -//############################################################################## -// EventDispatcher.js -//############################################################################## - +/*! +* EaselJS +* 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. +*/ + + +//############################################################################## +// extend.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Sets up the prototype chain and constructor property for a new class. + * + * This should be called right after creating the class constructor. + * + * function MySubClass() {} + * createjs.extend(MySubClass, MySuperClass); + * MySubClass.prototype.doSomething = function() { } + * + * var foo = new MySubClass(); + * console.log(foo instanceof MySuperClass); // true + * console.log(foo.prototype.constructor === MySubClass); // true + * + * @method extend + * @param {Function} subclass The subclass. + * @param {Function} superclass The superclass to extend. + * @return {Function} Returns the subclass's new prototype. + */ +createjs.extend = function(subclass, superclass) { + "use strict"; + + function o() { this.constructor = subclass; } + o.prototype = superclass.prototype; + return (subclass.prototype = new o()); +}; + +//############################################################################## +// promote.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`. + * It is recommended to use the super class's name as the prefix. + * An alias to the super class's constructor is always added in the format `prefix_constructor`. + * This allows the subclass to call super class methods without using `function.call`, providing better performance. + * + * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")` + * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the + * prototype of `MySubClass` as `MySuperClass_draw`. + * + * This should be called after the class's prototype is fully defined. + * + * function ClassA(name) { + * this.name = name; + * } + * ClassA.prototype.greet = function() { + * return "Hello "+this.name; + * } + * + * function ClassB(name, punctuation) { + * this.ClassA_constructor(name); + * this.punctuation = punctuation; + * } + * createjs.extend(ClassB, ClassA); + * ClassB.prototype.greet = function() { + * return this.ClassA_greet()+this.punctuation; + * } + * createjs.promote(ClassB, "ClassA"); + * + * var foo = new ClassB("World", "!?!"); + * console.log(foo.greet()); // Hello World!?! + * + * @method promote + * @param {Function} subclass The class to promote super class methods on. + * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass. + * @return {Function} Returns the subclass. + */ +createjs.promote = function(subclass, prefix) { + "use strict"; + + var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__; + if (supP) { + subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable + for (var n in supP) { + if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; } + } + } + return subclass; +}; + +//############################################################################## +// indexOf.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of + * that value. Returns -1 if value is not found. + * + * var i = createjs.indexOf(myArray, myElementToFind); + * + * @method indexOf + * @param {Array} array Array to search for searchElement + * @param searchElement Element to find in array. + * @return {Number} The first index of searchElement in array. + */ +createjs.indexOf = function (array, searchElement){ + "use strict"; + + for (var i = 0,l=array.length; i < l; i++) { + if (searchElement === array[i]) { + return i; + } + } + return -1; +}; + +//############################################################################## +// UID.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. UID.get()) + * and should not be instantiated. + * @class UID + * @static + **/ + function UID() { + throw "UID cannot be instantiated"; + } + + +// private static properties: + /** + * @property _nextID + * @type Number + * @protected + **/ + UID._nextID = 0; + + +// public static methods: + /** + * Returns the next unique id. + * @method get + * @return {Number} The next unique id + * @static + **/ + UID.get = function() { + return UID._nextID++; + }; + + + createjs.UID = UID; +}()); + +//############################################################################## +// deprecate.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/** + * @class Utility Methods + */ + +/** + * Wraps deprecated methods so they still be used, but throw warnings to developers. + * + * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod); + * + * The recommended approach for deprecated properties is: + * + * try { + * Obj ect.defineProperties(object, { + * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) }, + * readWriteProp: { + * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }), + * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; }) + * }); + * } catch (e) {} + * + * @method deprecate + * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how + * @param {String} [name=null] The name of the method or property to display in the console warning. + * to deprecate properties. + * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after + * logging the warning in the console. + */ +createjs.deprecate = function(fallbackMethod, name) { + "use strict"; + return function() { + var msg = "Deprecated property or method '"+name+"'. See docs for info."; + console && (console.warn ? console.warn(msg) : console.log(msg)); + return fallbackMethod && fallbackMethod.apply(this, arguments); + } +}; + +//############################################################################## +// Event.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + +// constructor: + /** + * Contains properties and methods shared by all events for use with + * {{#crossLink "EventDispatcher"}}{{/crossLink}}. + * + * Note that Event objects are often reused, so you should never + * rely on an event object's state outside of the call stack it was received in. + * @class Event + * @param {String} type The event type. + * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. + * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. + * @constructor + **/ + function Event(type, bubbles, cancelable) { + + + // public properties: + /** + * The type of event. + * @property type + * @type String + **/ + this.type = type; + + /** + * The object that generated an event. + * @property target + * @type Object + * @default null + * @readonly + */ + this.target = null; + + /** + * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will + * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event + * is generated from childObj, then a listener on parentObj would receive the event with + * target=childObj (the original target) and currentTarget=parentObj (where the listener was added). + * @property currentTarget + * @type Object + * @default null + * @readonly + */ + this.currentTarget = null; + + /** + * For bubbling events, this indicates the current event phase:
    + *
  1. capture phase: starting from the top parent to the target
  2. + *
  3. at target phase: currently being dispatched from the target
  4. + *
  5. bubbling phase: from the target to the top parent
  6. + *
+ * @property eventPhase + * @type Number + * @default 0 + * @readonly + */ + this.eventPhase = 0; + + /** + * Indicates whether the event will bubble through the display list. + * @property bubbles + * @type Boolean + * @default false + * @readonly + */ + this.bubbles = !!bubbles; + + /** + * Indicates whether the default behaviour of this event can be cancelled via + * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor. + * @property cancelable + * @type Boolean + * @default false + * @readonly + */ + this.cancelable = !!cancelable; + + /** + * The epoch time at which this event was created. + * @property timeStamp + * @type Number + * @default 0 + * @readonly + */ + this.timeStamp = (new Date()).getTime(); + + /** + * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called + * on this event. + * @property defaultPrevented + * @type Boolean + * @default false + * @readonly + */ + this.defaultPrevented = false; + + /** + * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or + * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event. + * @property propagationStopped + * @type Boolean + * @default false + * @readonly + */ + this.propagationStopped = false; + + /** + * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called + * on this event. + * @property immediatePropagationStopped + * @type Boolean + * @default false + * @readonly + */ + this.immediatePropagationStopped = false; + + /** + * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event. + * @property removed + * @type Boolean + * @default false + * @readonly + */ + this.removed = false; + } + var p = Event.prototype; + +// public methods: + /** + * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable. + * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will + * cancel the default behaviour associated with the event. + * @method preventDefault + **/ + p.preventDefault = function() { + this.defaultPrevented = this.cancelable&&true; + }; + + /** + * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true. + * Mirrors the DOM event standard. + * @method stopPropagation + **/ + p.stopPropagation = function() { + this.propagationStopped = true; + }; + + /** + * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and + * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true. + * Mirrors the DOM event standard. + * @method stopImmediatePropagation + **/ + p.stopImmediatePropagation = function() { + this.immediatePropagationStopped = this.propagationStopped = true; + }; + + /** + * Causes the active listener to be removed via removeEventListener(); + * + * myBtn.addEventListener("click", function(evt) { + * // do stuff... + * evt.remove(); // removes this listener. + * }); + * + * @method remove + **/ + p.remove = function() { + this.removed = true; + }; + + /** + * Returns a clone of the Event instance. + * @method clone + * @return {Event} a clone of the Event instance. + **/ + p.clone = function() { + return new Event(this.type, this.bubbles, this.cancelable); + }; + + /** + * Provides a chainable shortcut method for setting a number of properties on the instance. + * + * @method set + * @param {Object} props A generic object containing properties to copy to the instance. + * @return {Event} Returns the instance the method is called on (useful for chaining calls.) + * @chainable + */ + p.set = function(props) { + for (var n in props) { this[n] = props[n]; } + return this; + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Event (type="+this.type+")]"; + }; + + createjs.Event = Event; +}()); + +//############################################################################## +// EventDispatcher.js +//############################################################################## + this.createjs = this.createjs||{}; (function() { @@ -836,5106 +836,12 @@ this.createjs = this.createjs||{}; createjs.EventDispatcher = EventDispatcher; -}()); - -//############################################################################## -// Ticker.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick - * event to be notified when a set time interval has elapsed. - * - * Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval - * when under high CPU load. The Ticker class uses a static interface (ex. `Ticker.framerate = 30;`) and - * can not be instantiated. - * - *

Example

- * - * createjs.Ticker.addEventListener("tick", handleTick); - * function handleTick(event) { - * // Actions carried out each tick (aka frame) - * if (!event.paused) { - * // Actions carried out when the Ticker is not paused. - * } - * } - * - * @class Ticker - * @uses EventDispatcher - * @static - **/ - function Ticker() { - throw "Ticker cannot be instantiated."; - } - - -// constants: - /** - * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It - * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and - * dispatches the tick when the time is within a certain threshold. - * - * This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, - * but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while - * gaining the benefits of that API (screen synch, background throttling). - * - * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so - * framerates of 10, 12, 15, 20, and 30 work well. - * - * Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not - * supported. - * @property RAF_SYNCHED - * @static - * @type {String} - * @default "synched" - * @readonly - **/ - Ticker.RAF_SYNCHED = "synched"; - - /** - * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely. - * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based. - * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}} - * event object's "delta" properties to make this easier. - * - * Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not - * supported. - * @property RAF - * @static - * @type {String} - * @default "raf" - * @readonly - **/ - Ticker.RAF = "raf"; - - /** - * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not - * provide the benefits of requestAnimationFrame (screen synch, background throttling). - * @property TIMEOUT - * @static - * @type {String} - * @default "timeout" - * @readonly - **/ - Ticker.TIMEOUT = "timeout"; - - -// static events: - /** - * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using - * {{#crossLink "Ticker/paused:property"}}{{/crossLink}}. - * - *

Example

- * - * createjs.Ticker.addEventListener("tick", handleTick); - * function handleTick(event) { - * console.log("Paused:", event.paused, event.delta); - * } - * - * @event tick - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @param {Boolean} paused Indicates whether the ticker is currently paused. - * @param {Number} delta The time elapsed in ms since the last tick. - * @param {Number} time The total time in ms since Ticker was initialized. - * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example, - * you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`. - * @since 0.6.0 - */ - - -// public static properties: - /** - * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See - * {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, {{#crossLink "Ticker/RAF:property"}}{{/crossLink}}, and - * {{#crossLink "Ticker/RAF_SYNCHED:property"}}{{/crossLink}} for mode details. - * @property timingMode - * @static - * @type {String} - * @default Ticker.TIMEOUT - **/ - Ticker.timingMode = null; - - /** - * Specifies a maximum value for the delta property in the tick event object. This is useful when building time - * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep, - * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value - * (ex. maxDelta=50 when running at 40fps). - * - * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta - * when using both delta and other values. - * - * If 0, there is no maximum. - * @property maxDelta - * @static - * @type {number} - * @default 0 - */ - Ticker.maxDelta = 0; - - /** - * When the ticker is paused, all listeners will still receive a tick event, but the paused property - * of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}}, - * {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more - * info. - * - *

Example

- * - * createjs.Ticker.addEventListener("tick", handleTick); - * createjs.Ticker.paused = true; - * function handleTick(event) { - * console.log(event.paused, - * createjs.Ticker.getTime(false), - * createjs.Ticker.getTime(true)); - * } - * - * @property paused - * @static - * @type {Boolean} - * @default false - **/ - Ticker.paused = false; - - -// mix-ins: - // EventDispatcher methods: - Ticker.removeEventListener = null; - Ticker.removeAllEventListeners = null; - Ticker.dispatchEvent = null; - Ticker.hasEventListener = null; - Ticker._listeners = null; - createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods. - Ticker._addEventListener = Ticker.addEventListener; - Ticker.addEventListener = function() { - !Ticker._inited&&Ticker.init(); - return Ticker._addEventListener.apply(Ticker, arguments); - }; - - -// private static properties: - /** - * @property _inited - * @static - * @type {Boolean} - * @private - **/ - Ticker._inited = false; - - /** - * @property _startTime - * @static - * @type {Number} - * @private - **/ - Ticker._startTime = 0; - - /** - * @property _pausedTime - * @static - * @type {Number} - * @private - **/ - Ticker._pausedTime=0; - - /** - * The number of ticks that have passed - * @property _ticks - * @static - * @type {Number} - * @private - **/ - Ticker._ticks = 0; - - /** - * The number of ticks that have passed while Ticker has been paused - * @property _pausedTicks - * @static - * @type {Number} - * @private - **/ - Ticker._pausedTicks = 0; - - /** - * @property _interval - * @static - * @type {Number} - * @private - **/ - Ticker._interval = 50; - - /** - * @property _lastTime - * @static - * @type {Number} - * @private - **/ - Ticker._lastTime = 0; - - /** - * @property _times - * @static - * @type {Array} - * @private - **/ - Ticker._times = null; - - /** - * @property _tickTimes - * @static - * @type {Array} - * @private - **/ - Ticker._tickTimes = null; - - /** - * Stores the timeout or requestAnimationFrame id. - * @property _timerId - * @static - * @type {Number} - * @private - **/ - Ticker._timerId = null; - - /** - * True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode - * if that property changed and a tick hasn't fired. - * @property _raf - * @static - * @type {Boolean} - * @private - **/ - Ticker._raf = true; - - -// static getter / setters: - /** - * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead. - * @method _setInterval - * @private - * @static - * @param {Number} interval - **/ - Ticker._setInterval = function(interval) { - Ticker._interval = interval; - if (!Ticker._inited) { return; } - Ticker._setupTick(); - }; - // Ticker.setInterval is @deprecated. Remove for 1.1+ - Ticker.setInterval = createjs.deprecate(Ticker._setInterval, "Ticker.setInterval"); - - /** - * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead. - * @method _getInterval - * @private - * @static - * @return {Number} - **/ - Ticker._getInterval = function() { - return Ticker._interval; - }; - // Ticker.getInterval is @deprecated. Remove for 1.1+ - Ticker.getInterval = createjs.deprecate(Ticker._getInterval, "Ticker.getInterval"); - - /** - * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead. - * @method _setFPS - * @private - * @static - * @param {Number} value - **/ - Ticker._setFPS = function(value) { - Ticker._setInterval(1000/value); - }; - // Ticker.setFPS is @deprecated. Remove for 1.1+ - Ticker.setFPS = createjs.deprecate(Ticker._setFPS, "Ticker.setFPS"); - - /** - * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead. - * @method _getFPS - * @static - * @private - * @return {Number} - **/ - Ticker._getFPS = function() { - return 1000/Ticker._interval; - }; - // Ticker.getFPS is @deprecated. Remove for 1.1+ - Ticker.getFPS = createjs.deprecate(Ticker._getFPS, "Ticker.getFPS"); - - /** - * Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS). - * Note that actual time between ticks may be more than specified depending on CPU load. - * This property is ignored if the ticker is using the `RAF` timing mode. - * @property interval - * @static - * @type {Number} - **/ - - /** - * Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where - * `framerate == 1000/interval`. - * @property framerate - * @static - * @type {Number} - **/ - try { - Object.defineProperties(Ticker, { - interval: { get: Ticker._getInterval, set: Ticker._setInterval }, - framerate: { get: Ticker._getFPS, set: Ticker._setFPS } - }); - } catch (e) { console.log(e); } - - -// public static methods: - /** - * Starts the tick. This is called automatically when the first listener is added. - * @method init - * @static - **/ - Ticker.init = function() { - if (Ticker._inited) { return; } - Ticker._inited = true; - Ticker._times = []; - Ticker._tickTimes = []; - Ticker._startTime = Ticker._getTime(); - Ticker._times.push(Ticker._lastTime = 0); - Ticker.interval = Ticker._interval; - }; - - /** - * Stops the Ticker and removes all listeners. Use init() to restart the Ticker. - * @method reset - * @static - **/ - Ticker.reset = function() { - if (Ticker._raf) { - var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame; - f&&f(Ticker._timerId); - } else { - clearTimeout(Ticker._timerId); - } - Ticker.removeAllEventListeners("tick"); - Ticker._timerId = Ticker._times = Ticker._tickTimes = null; - Ticker._startTime = Ticker._lastTime = Ticker._ticks = Ticker._pausedTime = 0; - Ticker._inited = false; - }; - - /** - * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS - * because it only measures the time spent within the tick execution stack. - * - * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between - * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that - * there may be up to 35ms of "idle" time between the end of one tick and the start of the next. - * - * Example 2: With a target FPS of 30, {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} returns 10fps, which - * indicates an average of 100ms between the end of one tick and the end of the next. However, {{#crossLink "Ticker/getMeasuredTickTime"}}{{/crossLink}} - * returns 20ms. This would indicate that something other than the tick is using ~80ms (another script, DOM - * rendering, etc). - * @method getMeasuredTickTime - * @static - * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick. - * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1. - * @return {Number} The average time spent in a tick in milliseconds. - **/ - Ticker.getMeasuredTickTime = function(ticks) { - var ttl=0, times=Ticker._tickTimes; - if (!times || times.length < 1) { return -1; } - - // by default, calculate average for the past ~1 second: - ticks = Math.min(times.length, ticks||(Ticker._getFPS()|0)); - for (var i=0; i= (Ticker._interval-1)*0.97) { - Ticker._tick(); - } - }; - - /** - * @method _handleRAF - * @static - * @private - **/ - Ticker._handleRAF = function() { - Ticker._timerId = null; - Ticker._setupTick(); - Ticker._tick(); - }; - - /** - * @method _handleTimeout - * @static - * @private - **/ - Ticker._handleTimeout = function() { - Ticker._timerId = null; - Ticker._setupTick(); - Ticker._tick(); - }; - - /** - * @method _setupTick - * @static - * @private - **/ - Ticker._setupTick = function() { - if (Ticker._timerId != null) { return; } // avoid duplicates - - var mode = Ticker.timingMode; - if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) { - var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; - if (f) { - Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch); - Ticker._raf = true; - return; - } - } - Ticker._raf = false; - Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval); - }; - - /** - * @method _tick - * @static - * @private - **/ - Ticker._tick = function() { - var paused = Ticker.paused; - var time = Ticker._getTime(); - var elapsedTime = time-Ticker._lastTime; - Ticker._lastTime = time; - Ticker._ticks++; - - if (paused) { - Ticker._pausedTicks++; - Ticker._pausedTime += elapsedTime; - } - - if (Ticker.hasEventListener("tick")) { - var event = new createjs.Event("tick"); - var maxDelta = Ticker.maxDelta; - event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime; - event.paused = paused; - event.time = time; - event.runTime = time-Ticker._pausedTime; - Ticker.dispatchEvent(event); - } - - Ticker._tickTimes.unshift(Ticker._getTime()-time); - while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); } - - Ticker._times.unshift(time); - while (Ticker._times.length > 100) { Ticker._times.pop(); } - }; - - /** - * @method _getTime - * @static - * @private - **/ - var w=window, now=w.performance.now || w.performance.mozNow || w.performance.msNow || w.performance.oNow || w.performance.webkitNow; - Ticker._getTime = function() { - return ((now&&now.call(w.performance))||(new Date().getTime())) - Ticker._startTime; - }; - - - createjs.Ticker = Ticker; -}()); - -//############################################################################## -// VideoBuffer.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * When an HTML video seeks, including when looping, there is an indeterminate period before a new frame is available. - * This can result in the video blinking or flashing when it is drawn to a canvas. The VideoBuffer class resolves - * this issue by drawing each frame to an off-screen canvas and preserving the prior frame during a seek. - * - * var myBuffer = new createjs.VideoBuffer(myVideo); - * var myBitmap = new Bitmap(myBuffer); - * - * @class VideoBuffer - * @param {HTMLVideoElement} video The HTML video element to buffer. - * @constructor - **/ - function VideoBuffer(video) { - - // private properties: - /** - * Used by Bitmap to determine when the video buffer is ready to be drawn. Not intended for general use. - * @property readyState - * @protected - * @type {Number} - * @default 0 - **/ - this.readyState = video.readyState; - - /** - * @property _video - * @protected - * @type {HTMLVideoElement} - * @default 0 - **/ - this._video = video; - - /** - * @property _canvas - * @protected - * @type {HTMLCanvasElement} - * @default 0 - **/ - this._canvas = null; - - /** - * @property _lastTime - * @protected - * @type {Number} - * @default -1 - **/ - this._lastTime = -1; - - if (this.readyState < 2) { video.addEventListener("canplaythrough", this._videoReady.bind(this)); } //once:true isn't supported everywhere, but its a non-critical optimization here. - } - var p = VideoBuffer.prototype; - - -// public methods: - /** - * Gets an HTML canvas element showing the current video frame, or the previous frame if in a seek / loop. - * Primarily for use by {{#crossLink "Bitmap"}}{{/crossLink}}. - * @method getImage - **/ - p.getImage = function() { - if (this.readyState < 2) { return; } - var canvas=this._canvas, video = this._video; - if (!canvas) { - canvas = this._canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - } - if (video.readyState >= 2 && video.currentTime !== this._lastTime) { - var ctx = canvas.getContext("2d"); - ctx.clearRect(0,0,canvas.width,canvas.height); - ctx.drawImage(video,0,0,canvas.width,canvas.height); - this._lastTime = video.currentTime; - } - return canvas; - }; - -// private methods: - /** - * @method _videoReady - * @protected - **/ - p._videoReady = function() { - this.readyState = 2; - }; - - - createjs.VideoBuffer = VideoBuffer; -}()); - -//############################################################################## -// MouseEvent.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties, - * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings. - * @class MouseEvent - * @param {String} type The event type. - * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. - * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. - * @param {Number} stageX The normalized x position relative to the stage. - * @param {Number} stageY The normalized y position relative to the stage. - * @param {MouseEvent} nativeEvent The native DOM event related to this mouse event. - * @param {Number} pointerID The unique id for the pointer. - * @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment. - * @param {Number} rawX The raw x position relative to the stage. - * @param {Number} rawY The raw y position relative to the stage. - * @param {DisplayObject} relatedTarget The secondary target for the event. - * @extends Event - * @constructor - **/ - function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) { - this.Event_constructor(type, bubbles, cancelable); - - - // public properties: - /** - * The normalized x position on the stage. This will always be within the range 0 to stage width. - * @property stageX - * @type Number - */ - this.stageX = stageX; - - /** - * The normalized y position on the stage. This will always be within the range 0 to stage height. - * @property stageY - * @type Number - **/ - this.stageY = stageY; - - /** - * The raw x position relative to the stage. Normally this will be the same as the stageX value, unless - * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds. - * @property rawX - * @type Number - */ - this.rawX = (rawX==null)?stageX:rawX; - - /** - * The raw y position relative to the stage. Normally this will be the same as the stageY value, unless - * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds. - * @property rawY - * @type Number - */ - this.rawY = (rawY==null)?stageY:rawY; - - /** - * The native MouseEvent generated by the browser. The properties and API for this - * event may differ between browsers. This property will be null if the - * EaselJS property was not directly generated from a native MouseEvent. - * @property nativeEvent - * @type HtmlMouseEvent - * @default null - **/ - this.nativeEvent = nativeEvent; - - /** - * The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system - * supplied id value. - * @property pointerID - * @type {Number} - */ - this.pointerID = pointerID; - - /** - * Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse. - * For touch pointers, the first pointer in the current stack will be considered the primary pointer. - * @property primary - * @type {Boolean} - */ - this.primary = !!primary; - - /** - * The secondary target for the event, if applicable. This is used for mouseout/rollout - * events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited, - * and stagemousedown/stagemouseup events for the object that was the under the cursor, if any. - * - * Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set). - * @property relatedTarget - * @type {DisplayObject} - */ - this.relatedTarget = relatedTarget; - } - var p = createjs.extend(MouseEvent, createjs.Event); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// getter / setters: - /** - * Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher). - * @property localX - * @type {Number} - * @readonly - */ - p._get_localX = function() { - return this.currentTarget.globalToLocal(this.rawX, this.rawY).x; - }; - - /** - * Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher). - * @property localY - * @type {Number} - * @readonly - */ - p._get_localY = function() { - return this.currentTarget.globalToLocal(this.rawX, this.rawY).y; - }; - - /** - * Indicates whether the event was generated by a touch input (versus a mouse input). - * @property isTouch - * @type {Boolean} - * @readonly - */ - p._get_isTouch = function() { - return this.pointerID !== -1; - }; - - - try { - Object.defineProperties(p, { - localX: { get: p._get_localX }, - localY: { get: p._get_localY }, - isTouch: { get: p._get_isTouch } - }); - } catch (e) {} // TODO: use Log - - -// public methods: - /** - * Returns a clone of the MouseEvent instance. - * @method clone - * @return {MouseEvent} a clone of the MouseEvent instance. - **/ - p.clone = function() { - return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]"; - }; - - - createjs.MouseEvent = createjs.promote(MouseEvent, "Event"); -}()); - -//############################################################################## -// Matrix2D.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices. - * - * This matrix can be visualized as: - * - * [ a c tx - * b d ty - * 0 0 1 ] - * - * Note the locations of b and c. - * - * @class Matrix2D - * @param {Number} [a=1] Specifies the a property for the new matrix. - * @param {Number} [b=0] Specifies the b property for the new matrix. - * @param {Number} [c=0] Specifies the c property for the new matrix. - * @param {Number} [d=1] Specifies the d property for the new matrix. - * @param {Number} [tx=0] Specifies the tx property for the new matrix. - * @param {Number} [ty=0] Specifies the ty property for the new matrix. - * @constructor - **/ - function Matrix2D(a, b, c, d, tx, ty) { - this.setValues(a,b,c,d,tx,ty); - - // public properties: - // assigned in the setValues method. - /** - * Position (0, 0) in a 3x3 affine transformation matrix. - * @property a - * @type Number - **/ - - /** - * Position (0, 1) in a 3x3 affine transformation matrix. - * @property b - * @type Number - **/ - - /** - * Position (1, 0) in a 3x3 affine transformation matrix. - * @property c - * @type Number - **/ - - /** - * Position (1, 1) in a 3x3 affine transformation matrix. - * @property d - * @type Number - **/ - - /** - * Position (2, 0) in a 3x3 affine transformation matrix. - * @property tx - * @type Number - **/ - - /** - * Position (2, 1) in a 3x3 affine transformation matrix. - * @property ty - * @type Number - **/ - } - var p = Matrix2D.prototype; - -// constants: - /** - * Multiplier for converting degrees to radians. Used internally by Matrix2D. - * @property DEG_TO_RAD - * @static - * @final - * @type Number - * @readonly - **/ - Matrix2D.DEG_TO_RAD = Math.PI/180; - - -// static public properties: - /** - * An identity matrix, representing a null transformation. - * @property identity - * @static - * @type Matrix2D - * @readonly - **/ - Matrix2D.identity = null; // set at bottom of class definition. - - -// public methods: - /** - * Sets the specified values on this instance. - * @method setValues - * @param {Number} [a=1] Specifies the a property for the new matrix. - * @param {Number} [b=0] Specifies the b property for the new matrix. - * @param {Number} [c=0] Specifies the c property for the new matrix. - * @param {Number} [d=1] Specifies the d property for the new matrix. - * @param {Number} [tx=0] Specifies the tx property for the new matrix. - * @param {Number} [ty=0] Specifies the ty property for the new matrix. - * @return {Matrix2D} This instance. Useful for chaining method calls. - */ - p.setValues = function(a, b, c, d, tx, ty) { - // don't forget to update docs in the constructor if these change: - this.a = (a == null) ? 1 : a; - this.b = b || 0; - this.c = c || 0; - this.d = (d == null) ? 1 : d; - this.tx = tx || 0; - this.ty = ty || 0; - return this; - }; - - /** - * Appends the specified matrix properties to this matrix. All parameters are required. - * This is the equivalent of multiplying `(this matrix) * (specified matrix)`. - * @method append - * @param {Number} a - * @param {Number} b - * @param {Number} c - * @param {Number} d - * @param {Number} tx - * @param {Number} ty - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.append = function(a, b, c, d, tx, ty) { - var a1 = this.a; - var b1 = this.b; - var c1 = this.c; - var d1 = this.d; - if (a != 1 || b != 0 || c != 0 || d != 1) { - this.a = a1*a+c1*b; - this.b = b1*a+d1*b; - this.c = a1*c+c1*d; - this.d = b1*c+d1*d; - } - this.tx = a1*tx+c1*ty+this.tx; - this.ty = b1*tx+d1*ty+this.ty; - return this; - }; - - /** - * Prepends the specified matrix properties to this matrix. - * This is the equivalent of multiplying `(specified matrix) * (this matrix)`. - * All parameters are required. - * @method prepend - * @param {Number} a - * @param {Number} b - * @param {Number} c - * @param {Number} d - * @param {Number} tx - * @param {Number} ty - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.prepend = function(a, b, c, d, tx, ty) { - var a1 = this.a; - var c1 = this.c; - var tx1 = this.tx; - - this.a = a*a1+c*this.b; - this.b = b*a1+d*this.b; - this.c = a*c1+c*this.d; - this.d = b*c1+d*this.d; - this.tx = a*tx1+c*this.ty+tx; - this.ty = b*tx1+d*this.ty+ty; - return this; - }; - - /** - * Appends the specified matrix to this matrix. - * This is the equivalent of multiplying `(this matrix) * (specified matrix)`. - * @method appendMatrix - * @param {Matrix2D} matrix - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.appendMatrix = function(matrix) { - return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); - }; - - /** - * Prepends the specified matrix to this matrix. - * This is the equivalent of multiplying `(specified matrix) * (this matrix)`. - * For example, you could calculate the combined transformation for a child object using: - * - * var o = myDisplayObject; - * var mtx = o.getMatrix(); - * while (o = o.parent) { - * // prepend each parent's transformation in turn: - * o.prependMatrix(o.getMatrix()); - * } - * @method prependMatrix - * @param {Matrix2D} matrix - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.prependMatrix = function(matrix) { - return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); - }; - - /** - * Generates matrix properties from the specified display object transform properties, and appends them to this matrix. - * For example, you can use this to generate a matrix representing the transformations of a display object: - * - * var mtx = new createjs.Matrix2D(); - * mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation); - * @method appendTransform - * @param {Number} x - * @param {Number} y - * @param {Number} scaleX - * @param {Number} scaleY - * @param {Number} rotation - * @param {Number} skewX - * @param {Number} skewY - * @param {Number} regX Optional. - * @param {Number} regY Optional. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { - if (rotation%360) { - var r = rotation*Matrix2D.DEG_TO_RAD; - var cos = Math.cos(r); - var sin = Math.sin(r); - } else { - cos = 1; - sin = 0; - } - - if (skewX || skewY) { - // TODO: can this be combined into a single append operation? - skewX *= Matrix2D.DEG_TO_RAD; - skewY *= Matrix2D.DEG_TO_RAD; - this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); - this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); - } else { - this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); - } - - if (regX || regY) { - // append the registration offset: - this.tx -= regX*this.a+regY*this.c; - this.ty -= regX*this.b+regY*this.d; - } - return this; - }; - - /** - * Generates matrix properties from the specified display object transform properties, and prepends them to this matrix. - * For example, you could calculate the combined transformation for a child object using: - * - * var o = myDisplayObject; - * var mtx = new createjs.Matrix2D(); - * do { - * // prepend each parent's transformation in turn: - * mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY); - * } while (o = o.parent); - * - * Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}} - * values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does. - * @method prependTransform - * @param {Number} x - * @param {Number} y - * @param {Number} scaleX - * @param {Number} scaleY - * @param {Number} rotation - * @param {Number} skewX - * @param {Number} skewY - * @param {Number} regX Optional. - * @param {Number} regY Optional. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { - if (rotation%360) { - var r = rotation*Matrix2D.DEG_TO_RAD; - var cos = Math.cos(r); - var sin = Math.sin(r); - } else { - cos = 1; - sin = 0; - } - - if (regX || regY) { - // prepend the registration offset: - this.tx -= regX; this.ty -= regY; - } - if (skewX || skewY) { - // TODO: can this be combined into a single prepend operation? - skewX *= Matrix2D.DEG_TO_RAD; - skewY *= Matrix2D.DEG_TO_RAD; - this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); - this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); - } else { - this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); - } - return this; - }; - - /** - * Applies a clockwise rotation transformation to the matrix. - * @method rotate - * @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.rotate = function(angle) { - angle = angle*Matrix2D.DEG_TO_RAD; - var cos = Math.cos(angle); - var sin = Math.sin(angle); - - var a1 = this.a; - var b1 = this.b; - - this.a = a1*cos+this.c*sin; - this.b = b1*cos+this.d*sin; - this.c = -a1*sin+this.c*cos; - this.d = -b1*sin+this.d*cos; - return this; - }; - - /** - * Applies a skew transformation to the matrix. - * @method skew - * @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`. - * @param {Number} skewY The amount to skew vertically in degrees. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - */ - p.skew = function(skewX, skewY) { - skewX = skewX*Matrix2D.DEG_TO_RAD; - skewY = skewY*Matrix2D.DEG_TO_RAD; - this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0); - return this; - }; - - /** - * Applies a scale transformation to the matrix. - * @method scale - * @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it. - * @param {Number} y The amount to scale vertically. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.scale = function(x, y) { - this.a *= x; - this.b *= x; - this.c *= y; - this.d *= y; - //this.tx *= x; - //this.ty *= y; - return this; - }; - - /** - * Translates the matrix on the x and y axes. - * @method translate - * @param {Number} x - * @param {Number} y - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.translate = function(x, y) { - this.tx += this.a*x + this.c*y; - this.ty += this.b*x + this.d*y; - return this; - }; - - /** - * Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation). - * @method identity - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.identity = function() { - this.a = this.d = 1; - this.b = this.c = this.tx = this.ty = 0; - return this; - }; - - /** - * Inverts the matrix, causing it to perform the opposite transformation. - * @method invert - * @return {Matrix2D} This matrix. Useful for chaining method calls. - **/ - p.invert = function() { - var a1 = this.a; - var b1 = this.b; - var c1 = this.c; - var d1 = this.d; - var tx1 = this.tx; - var n = a1*d1-b1*c1; - - this.a = d1/n; - this.b = -b1/n; - this.c = -c1/n; - this.d = a1/n; - this.tx = (c1*this.ty-d1*tx1)/n; - this.ty = -(a1*this.ty-b1*tx1)/n; - return this; - }; - - /** - * Returns true if the matrix is an identity matrix. - * @method isIdentity - * @return {Boolean} - **/ - p.isIdentity = function() { - return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1; - }; - - /** - * Returns true if this matrix is equal to the specified matrix (all property values are equal). - * @method equals - * @param {Matrix2D} matrix The matrix to compare. - * @return {Boolean} - **/ - p.equals = function(matrix) { - return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d; - }; - - /** - * Transforms a point according to this matrix. - * @method transformPoint - * @param {Number} x The x component of the point to transform. - * @param {Number} y The y component of the point to transform. - * @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned. - * @return {Point} This matrix. Useful for chaining method calls. - **/ - p.transformPoint = function(x, y, pt) { - pt = pt||{}; - pt.x = x*this.a+y*this.c+this.tx; - pt.y = x*this.b+y*this.d+this.ty; - return pt; - }; - - /** - * Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values - * may not match the transform properties you used to generate the matrix, though they will produce the same visual - * results. - * @method decompose - * @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned. - * @return {Object} The target, or a new generic object with the transform properties applied. - */ - p.decompose = function(target) { - // TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative - if (target == null) { target = {}; } - target.x = this.tx; - target.y = this.ty; - target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b); - target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d); - - var skewX = Math.atan2(-this.c, this.d); - var skewY = Math.atan2(this.b, this.a); - - var delta = Math.abs(1-skewX/skewY); - if (delta < 0.00001) { // effectively identical, can use rotation: - target.rotation = skewY/Matrix2D.DEG_TO_RAD; - if (this.a < 0 && this.d >= 0) { - target.rotation += (target.rotation <= 0) ? 180 : -180; - } - target.skewX = target.skewY = 0; - } else { - target.skewX = skewX/Matrix2D.DEG_TO_RAD; - target.skewY = skewY/Matrix2D.DEG_TO_RAD; - } - return target; - }; - - /** - * Copies all properties from the specified matrix to this matrix. - * @method copy - * @param {Matrix2D} matrix The matrix to copy properties from. - * @return {Matrix2D} This matrix. Useful for chaining method calls. - */ - p.copy = function(matrix) { - return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); - }; - - /** - * Returns a clone of the Matrix2D instance. - * @method clone - * @return {Matrix2D} a clone of the Matrix2D instance. - **/ - p.clone = function() { - return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]"; - }; - - // this has to be populated after the class is defined: - Matrix2D.identity = new Matrix2D(); - - - createjs.Matrix2D = Matrix2D; -}()); - -//############################################################################## -// DisplayProps.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - /** - * Used for calculating and encapsulating display related properties. - * @class DisplayProps - * @param {Number} [visible=true] Visible value. - * @param {Number} [alpha=1] Alpha value. - * @param {Number} [shadow=null] A Shadow instance or null. - * @param {Number} [compositeOperation=null] A compositeOperation value or null. - * @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix. - * @constructor - **/ - function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) { - this.setValues(visible, alpha, shadow, compositeOperation, matrix); - - // public properties: - // assigned in the setValues method. - /** - * Property representing the alpha that will be applied to a display object. - * @property alpha - * @type Number - **/ - - /** - * Property representing the shadow that will be applied to a display object. - * @property shadow - * @type Shadow - **/ - - /** - * Property representing the compositeOperation that will be applied to a display object. - * You can find a list of valid composite operations at: - * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing - * @property compositeOperation - * @type String - **/ - - /** - * Property representing the value for visible that will be applied to a display object. - * @property visible - * @type Boolean - **/ - - /** - * The transformation matrix that will be applied to a display object. - * @property matrix - * @type Matrix2D - **/ - } - var p = DisplayProps.prototype; - -// initialization: - /** - * Reinitializes the instance with the specified values. - * @method setValues - * @param {Number} [visible=true] Visible value. - * @param {Number} [alpha=1] Alpha value. - * @param {Number} [shadow=null] A Shadow instance or null. - * @param {Number} [compositeOperation=null] A compositeOperation value or null. - * @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix. - * @return {DisplayProps} This instance. Useful for chaining method calls. - * @chainable - */ - p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) { - this.visible = visible == null ? true : !!visible; - this.alpha = alpha == null ? 1 : alpha; - this.shadow = shadow; - this.compositeOperation = compositeOperation; - this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D(); - return this; - }; - -// public methods: - /** - * Appends the specified display properties. This is generally used to apply a child's properties its parent's. - * @method append - * @param {Boolean} visible desired visible value - * @param {Number} alpha desired alpha value - * @param {Shadow} shadow desired shadow value - * @param {String} compositeOperation desired composite operation value - * @param {Matrix2D} [matrix] a Matrix2D instance - * @return {DisplayProps} This instance. Useful for chaining method calls. - * @chainable - */ - p.append = function(visible, alpha, shadow, compositeOperation, matrix) { - this.alpha *= alpha; - this.shadow = shadow || this.shadow; - this.compositeOperation = compositeOperation || this.compositeOperation; - this.visible = this.visible && visible; - matrix&&this.matrix.appendMatrix(matrix); - return this; - }; - - /** - * Prepends the specified display properties. This is generally used to apply a parent's properties to a child's. - * For example, to get the combined display properties that would be applied to a child, you could use: - * - * var o = myDisplayObject; - * var props = new createjs.DisplayProps(); - * do { - * // prepend each parent's props in turn: - * props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix()); - * } while (o = o.parent); - * - * @method prepend - * @param {Boolean} visible desired visible value - * @param {Number} alpha desired alpha value - * @param {Shadow} shadow desired shadow value - * @param {String} compositeOperation desired composite operation value - * @param {Matrix2D} [matrix] a Matrix2D instance - * @return {DisplayProps} This instance. Useful for chaining method calls. - * @chainable - */ - p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) { - this.alpha *= alpha; - this.shadow = this.shadow || shadow; - this.compositeOperation = this.compositeOperation || compositeOperation; - this.visible = this.visible && visible; - matrix&&this.matrix.prependMatrix(matrix); - return this; - }; - - /** - * Resets this instance and its matrix to default values. - * @method identity - * @return {DisplayProps} This instance. Useful for chaining method calls. - * @chainable - */ - p.identity = function() { - this.visible = true; - this.alpha = 1; - this.shadow = this.compositeOperation = null; - this.matrix.identity(); - return this; - }; - - /** - * Returns a clone of the DisplayProps instance. Clones the associated matrix. - * @method clone - * @return {DisplayProps} a clone of the DisplayProps instance. - **/ - p.clone = function() { - return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone()); - }; - -// private methods: - - createjs.DisplayProps = DisplayProps; -})(); - -//############################################################################## -// Point.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Represents a point on a 2 dimensional x / y coordinate system. - * - *

Example

- * - * var point = new createjs.Point(0, 100); - * - * @class Point - * @param {Number} [x=0] X position. - * @param {Number} [y=0] Y position. - * @constructor - **/ - function Point(x, y) { - this.setValues(x, y); - - - // public properties: - // assigned in the setValues method. - /** - * X position. - * @property x - * @type Number - **/ - - /** - * Y position. - * @property y - * @type Number - **/ - } - var p = Point.prototype; - -// public methods: - /** - * Sets the specified values on this instance. - * @method setValues - * @param {Number} [x=0] X position. - * @param {Number} [y=0] Y position. - * @return {Point} This instance. Useful for chaining method calls. - * @chainable - */ - p.setValues = function(x, y) { - this.x = x||0; - this.y = y||0; - return this; - }; - - /** - * Copies all properties from the specified point to this point. - * @method copy - * @param {Point} point The point to copy properties from. - * @return {Point} This point. Useful for chaining method calls. - * @chainable - */ - p.copy = function(point) { - this.x = point.x; - this.y = point.y; - return this; - }; - - /** - * Returns a clone of the Point instance. - * @method clone - * @return {Point} a clone of the Point instance. - **/ - p.clone = function() { - return new Point(this.x, this.y); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Point (x="+this.x+" y="+this.y+")]"; - }; - - - createjs.Point = Point; -}()); - -//############################################################################## -// Rectangle.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Represents a rectangle as defined by the points (x, y) and (x+width, y+height). - * - *

Example

- * - * var rect = new createjs.Rectangle(0, 0, 100, 100); - * - * @class Rectangle - * @param {Number} [x=0] X position. - * @param {Number} [y=0] Y position. - * @param {Number} [width=0] The width of the Rectangle. - * @param {Number} [height=0] The height of the Rectangle. - * @constructor - **/ - function Rectangle(x, y, width, height) { - this.setValues(x, y, width, height); - - - // public properties: - // assigned in the setValues method. - /** - * X position. - * @property x - * @type Number - **/ - - /** - * Y position. - * @property y - * @type Number - **/ - - /** - * Width. - * @property width - * @type Number - **/ - - /** - * Height. - * @property height - * @type Number - **/ - } - var p = Rectangle.prototype; - -// public methods: - /** - * Sets the specified values on this instance. - * @method setValues - * @param {Number} [x=0] X position. - * @param {Number} [y=0] Y position. - * @param {Number} [width=0] The width of the Rectangle. - * @param {Number} [height=0] The height of the Rectangle. - * @return {Rectangle} This instance. Useful for chaining method calls. - * @chainable - */ - p.setValues = function(x, y, width, height) { - // don't forget to update docs in the constructor if these change: - this.x = x||0; - this.y = y||0; - this.width = width||0; - this.height = height||0; - return this; - }; - - /** - * Extends the rectangle's bounds to include the described point or rectangle. - * @method extend - * @param {Number} x X position of the point or rectangle. - * @param {Number} y Y position of the point or rectangle. - * @param {Number} [width=0] The width of the rectangle. - * @param {Number} [height=0] The height of the rectangle. - * @return {Rectangle} This instance. Useful for chaining method calls. - * @chainable - */ - p.extend = function(x, y, width, height) { - width = width||0; - height = height||0; - if (x+width > this.x+this.width) { this.width = x+width-this.x; } - if (y+height > this.y+this.height) { this.height = y+height-this.y; } - if (x < this.x) { this.width += this.x-x; this.x = x; } - if (y < this.y) { this.height += this.y-y; this.y = y; } - return this; - }; - - /** - * Adds the specified padding to the rectangle's bounds. - * @method pad - * @param {Number} top - * @param {Number} left - * @param {Number} bottom - * @param {Number} right - * @return {Rectangle} This instance. Useful for chaining method calls. - * @chainable - */ - p.pad = function(top, left, bottom, right) { - this.x -= left; - this.y -= top; - this.width += left+right; - this.height += top+bottom; - return this; - }; - - /** - * Copies all properties from the specified rectangle to this rectangle. - * @method copy - * @param {Rectangle} rectangle The rectangle to copy properties from. - * @return {Rectangle} This rectangle. Useful for chaining method calls. - * @chainable - */ - p.copy = function(rectangle) { - return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height); - }; - - /** - * Returns true if this rectangle fully encloses the described point or rectangle. - * @method contains - * @param {Number} x X position of the point or rectangle. - * @param {Number} y Y position of the point or rectangle. - * @param {Number} [width=0] The width of the rectangle. - * @param {Number} [height=0] The height of the rectangle. - * @return {Boolean} True if the described point or rectangle is contained within this rectangle. - */ - p.contains = function(x, y, width, height) { - width = width||0; - height = height||0; - return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height); - }; - - /** - * Returns a new rectangle which contains this rectangle and the specified rectangle. - * @method union - * @param {Rectangle} rect The rectangle to calculate a union with. - * @return {Rectangle} A new rectangle describing the union. - */ - p.union = function(rect) { - return this.clone().extend(rect.x, rect.y, rect.width, rect.height); - }; - - /** - * Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle, - * or null if they do not intersect. - * @method intersection - * @param {Rectangle} rect The rectangle to calculate an intersection with. - * @return {Rectangle} A new rectangle describing the intersection or null. - */ - p.intersection = function(rect) { - var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height; - if (this.x > x1) { x1 = this.x; } - if (this.y > y1) { y1 = this.y; } - if (this.x + this.width < x2) { x2 = this.x + this.width; } - if (this.y + this.height < y2) { y2 = this.y + this.height; } - return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1); - }; - - /** - * Returns true if the specified rectangle intersects (has any overlap) with this rectangle. - * @method intersects - * @param {Rectangle} rect The rectangle to compare. - * @return {Boolean} True if the rectangles intersect. - */ - p.intersects = function(rect) { - return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height); - }; - - /** - * Returns true if the width or height are equal or less than 0. - * @method isEmpty - * @return {Boolean} True if the rectangle is empty. - */ - p.isEmpty = function() { - return this.width <= 0 || this.height <= 0; - }; - - /** - * Returns a clone of the Rectangle instance. - * @method clone - * @return {Rectangle} a clone of the Rectangle instance. - **/ - p.clone = function() { - return new Rectangle(this.x, this.y, this.width, this.height); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]"; - }; - - - createjs.Rectangle = Rectangle; -}()); - -//############################################################################## -// ButtonHelper.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or - * {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and - * automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, - * to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame. - * - * The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent - * garbage collection. - * - * Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. - * - *

Example

- * - * var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit"); - * myInstance.addEventListener("click", handleClick); - * function handleClick(event) { - * // Click Happened. - * } - * - * @class ButtonHelper - * @param {Sprite|MovieClip} target The instance to manage. - * @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button. - * @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button. - * @param {String} [downLabel="down"] The label or animation to go to when the user presses the button. - * @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing - * states. - * @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined, - * then the button's visible states will be used instead. Note that the same instance as the "target" argument can be - * used for the hitState. - * @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is - * null, then the default state of the hitArea will be used. * - * @constructor - */ - function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) { - if (!target.addEventListener) { return; } - - - // public properties: - /** - * The target for this button helper. - * @property target - * @type MovieClip | Sprite - * @readonly - **/ - this.target = target; - - /** - * The label name or frame number to display when the user mouses out of the target. Defaults to "over". - * @property overLabel - * @type String | Number - **/ - this.overLabel = overLabel == null ? "over" : overLabel; - - /** - * The label name or frame number to display when the user mouses over the target. Defaults to "out". - * @property outLabel - * @type String | Number - **/ - this.outLabel = outLabel == null ? "out" : outLabel; - - /** - * The label name or frame number to display when the user presses on the target. Defaults to "down". - * @property downLabel - * @type String | Number - **/ - this.downLabel = downLabel == null ? "down" : downLabel; - - /** - * If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false. - * @property play - * @default false - * @type Boolean - **/ - this.play = play; - - - // private properties - /** - * @property _isPressed - * @type Boolean - * @protected - **/ - this._isPressed = false; - - /** - * @property _isOver - * @type Boolean - * @protected - **/ - this._isOver = false; - - /** - * @property _enabled - * @type Boolean - * @protected - **/ - this._enabled = false; - - // setup: - target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes. - this.enabled = true; - this.handleEvent({}); - if (hitArea) { - if (hitLabel) { - hitArea.actionsEnabled = false; - hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel); - } - target.hitArea = hitArea; - } - } - var p = ButtonHelper.prototype; - -// getter / setters: - /** - * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead. - * @method setEnabled - * @param {Boolean} value The enabled property to set the instance to. - * @[rptected - * @protected - **/ - p._setEnabled = function(value) { - if (value == this._enabled) { return; } - var o = this.target; - this._enabled = value; - if (value) { - o.cursor = "pointer"; - o.addEventListener("rollover", this); - o.addEventListener("rollout", this); - o.addEventListener("mousedown", this); - o.addEventListener("pressup", this); - if (o._reset) { o.__reset = o._reset; o._reset = this._reset;} - } else { - o.cursor = null; - o.removeEventListener("rollover", this); - o.removeEventListener("rollout", this); - o.removeEventListener("mousedown", this); - o.removeEventListener("pressup", this); - if (o.__reset) { o._reset = o.__reset; delete(o.__reset); } - } - }; - // ButtonHelper.setEnabled is @deprecated. Remove for 1.1+ - p.setEnabled = createjs.deprecate(p._setEnabled, "ButtonHelper.setEnabled"); - - /** - * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead. - * @method getEnabled - * @protected - * @return {Boolean} - **/ - p._getEnabled = function() { - return this._enabled; - }; - // ButtonHelper.getEnabled is @deprecated. Remove for 1.1+ - p.getEnabled = createjs.deprecate(p._getEnabled, "ButtonHelper.getEnabled"); - - /** - * Enables or disables the button functionality on the target. - * @property enabled - * @type {Boolean} - **/ - try { - Object.defineProperties(p, { - enabled: { get: p._getEnabled, set: p._setEnabled } - }); - } catch (e) {} // TODO: use Log - - -// public methods: - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[ButtonHelper]"; - }; - - -// private methods: - /** - * @method handleEvent - * @param {Object} evt The mouse event to handle. - * @protected - **/ - p.handleEvent = function(evt) { - var label, t = this.target, type = evt.type; - if (type == "mousedown") { - this._isPressed = true; - label = this.downLabel; - } else if (type == "pressup") { - this._isPressed = false; - label = this._isOver ? this.overLabel : this.outLabel; - } else if (type == "rollover") { - this._isOver = true; - label = this._isPressed ? this.downLabel : this.overLabel; - } else { // rollout and default - this._isOver = false; - label = this._isPressed ? this.overLabel : this.outLabel; - } - if (this.play) { - t.gotoAndPlay&&t.gotoAndPlay(label); - } else { - t.gotoAndStop&&t.gotoAndStop(label); - } - }; - - /** - * Injected into target. Preserves the paused state through a reset. - * @method _reset - * @protected - **/ - p._reset = function() { - // TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures. - var p = this.paused; - this.__reset(); - this.paused = p; - }; - - - createjs.ButtonHelper = ButtonHelper; -}()); - -//############################################################################## -// Shadow.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}} - * via its shadow property. - * - *

Example

- * - * myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10); - * - * @class Shadow - * @constructor - * @param {String} color The color of the shadow. This can be any valid CSS color value. - * @param {Number} offsetX The x offset of the shadow in pixels. - * @param {Number} offsetY The y offset of the shadow in pixels. - * @param {Number} blur The size of the blurring effect. - **/ - function Shadow(color, offsetX, offsetY, blur) { - - - // public properties: - /** - * The color of the shadow. This can be any valid CSS color value. - * @property color - * @type String - * @default null - */ - this.color = color||"black"; - - /** The x offset of the shadow. - * @property offsetX - * @type Number - * @default 0 - */ - this.offsetX = offsetX||0; - - /** The y offset of the shadow. - * @property offsetY - * @type Number - * @default 0 - */ - this.offsetY = offsetY||0; - - /** The blur of the shadow. - * @property blur - * @type Number - * @default 0 - */ - this.blur = blur||0; - } - var p = Shadow.prototype; - -// static public properties: - /** - * An identity shadow object (all properties are set to 0). - * @property identity - * @type Shadow - * @static - * @final - * @readonly - **/ - Shadow.identity = new Shadow("transparent", 0, 0, 0); - - -// public methods: - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Shadow]"; - }; - - /** - * Returns a clone of this Shadow instance. - * @method clone - * @return {Shadow} A clone of the current Shadow instance. - **/ - p.clone = function() { - return new Shadow(this.color, this.offsetX, this.offsetY, this.blur); - }; - - - createjs.Shadow = Shadow; -}()); - -//############################################################################## -// SpriteSheet.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually - * animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100 - * images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high). - * - * The data passed to the SpriteSheet constructor defines: - *
    - *
  1. The source image or images to use.
  2. - *
  3. The positions of individual image frames.
  4. - *
  5. Sequences of frames that form named animations. Optional.
  6. - *
  7. The target playback framerate. Optional.
  8. - *
- *

SpriteSheet Format

- * SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties - * (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON. - * - *

images

- * An array of source images. Images can be either an HTMlimage - * instance, or a uri to an image. The former is recommended to control preloading. - * - * images: [image1, "path/to/image2.png"], - * - *

frames

- * Defines the individual frames. There are two supported formats for frame data: - * When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`, - * and `count` properties. - * - * - * - * frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0} - * - * If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array - * with 4 required and 3 optional entries, in the order: - * - * - * - * frames: [ - * // x, y, width, height, imageIndex*, regX*, regY* - * [64, 0, 96, 64], - * [0, 0, 64, 64, 1, 32, 32] - * // etc. - * ] - * - *

animations

- * Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an - * animation of the same name. Each animation must specify the frames to play, and may - * also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and - * the name of the `next` animation to sequence to after it completes. - * - * There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate: - *
    - *
  1. for a single frame animation, you can simply specify the frame index - * - * animations: { - * sit: 7 - * } - * - *
  2. - *
  3. - * for an animation of consecutive frames, you can use an array with two required, and two optional entries - * in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive. - * - * animations: { - * // start, end, next*, speed* - * run: [0, 8], - * jump: [9, 12, "run", 2] - * } - * - *
  4. - *
  5. - * for non-consecutive frames, you can use an object with a `frames` property defining an array of frame - * indexes to play in order. The object can also specify `next` and `speed` properties. - * - * animations: { - * walk: { - * frames: [1,2,3,3,2,1] - * }, - * shoot: { - * frames: [1,4,5,6], - * next: "walk", - * speed: 0.5 - * } - * } - * - *
  6. - *
- * Note: the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency` - * property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in - * earlier versions, but is 4x normal speed in EaselJS 0.7.0+. - * - *

framerate

- * Optional. Indicates the default framerate to play this spritesheet at in frames per second. See - * {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information. - * - * framerate: 20 - * - * Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}} - * event generated by the {{#crossLink "Ticker"}}{{/crossLink}}. - * - * createjs.Ticker.on("tick", handleTick); - * function handleTick(event) { - * stage.update(event); - * } - * - *

Example

- * To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three - * animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and - * sequencing back to run. - * - * var data = { - * images: ["sprites.jpg"], - * frames: {width:50, height:50}, - * animations: { - * stand:0, - * run:[1,5], - * jump:[6,8,"run"] - * } - * }; - * var spriteSheet = new createjs.SpriteSheet(data); - * var animation = new createjs.Sprite(spriteSheet, "run"); - * - *

Generating SpriteSheet Images

- * Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or - * coordinates manually, however there are a number of tools that facilitate this. - * - * - *

Cross Origin Issues

- * Warning: Images loaded cross-origin will throw cross-origin security errors when interacted with - * using: - * - * You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or - * setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems. - * - * var image = new Image(); - * img.crossOrigin="Anonymous"; - * img.src = "http://server-with-CORS-support.com/path/to/image.jpg"; - * - * If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must - * support cross-origin requests, or this will not work. For more information, check out - * CORS overview on MDN. - * - * @class SpriteSheet - * @constructor - * @param {Object} data An object describing the SpriteSheet data. - * @extends EventDispatcher - **/ - function SpriteSheet(data) { - this.EventDispatcher_constructor(); - - - // public properties: - /** - * Indicates whether all images are finished loading. - * @property complete - * @type Boolean - * @readonly - **/ - this.complete = true; - - /** - * Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class - * {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information. - * @property framerate - * @type Number - **/ - this.framerate = 0; - - - // private properties: - /** - * @property _animations - * @protected - * @type Array - **/ - this._animations = null; - - /** - * @property _frames - * @protected - * @type Array - **/ - this._frames = null; - - /** - * @property _images - * @protected - * @type Array - **/ - this._images = null; - - /** - * @property _data - * @protected - * @type Object - **/ - this._data = null; - - /** - * @property _loadCount - * @protected - * @type Number - **/ - this._loadCount = 0; - - // only used for simple frame defs: - /** - * @property _frameHeight - * @protected - * @type Number - **/ - this._frameHeight = 0; - - /** - * @property _frameWidth - * @protected - * @type Number - **/ - this._frameWidth = 0; - - /** - * @property _numFrames - * @protected - * @type Number - **/ - this._numFrames = 0; - - /** - * @property _regX - * @protected - * @type Number - **/ - this._regX = 0; - - /** - * @property _regY - * @protected - * @type Number - **/ - this._regY = 0; - - /** - * @property _spacing - * @protected - * @type Number - **/ - this._spacing = 0; - - /** - * @property _margin - * @protected - * @type Number - **/ - this._margin = 0; - - // setup: - this._parseData(data); - } - var p = createjs.extend(SpriteSheet, createjs.EventDispatcher); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// events: - /** - * Dispatched when all images are loaded. Note that this only fires if the images - * were not fully loaded when the sprite sheet was initialized. You should check the complete property - * to prior to adding a listener. Ex. - * - * var sheet = new createjs.SpriteSheet(data); - * if (!sheet.complete) { - * // not preloaded, listen for the complete event: - * sheet.addEventListener("complete", handler); - * } - * - * @event complete - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.6.0 - */ - - /** - * Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} - * when doing on-demand rendering. - * @event getframe - * @param {Number} index The frame index. - * @param {Object} frame The frame object that getFrame will return. - */ - - /** - * Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that - * encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}} - * event once all images are finished processing, even if an error is encountered. - * @event error - * @param {String} src The source of the image that failed to load. - * @since 0.8.2 - */ - - -// getter / setters: - /** - * Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead. - * @method _getAnimations - * @protected - * @return {Array} - **/ - p._getAnimations = function() { - return this._animations.slice(); - }; - // SpriteSheet.getAnimations is @deprecated. Remove for 1.1+ - p.getAnimations = createjs.deprecate(p._getAnimations, "SpriteSheet.getAnimations"); - - /** - * Returns an array of all available animation names available on this sprite sheet as strings. - * @property animations - * @type {Array} - * @readonly - **/ - try { - Object.defineProperties(p, { - animations: { get: p._getAnimations } - }); - } catch (e) {} - - -// public methods: - /** - * Returns the total number of frames in the specified animation, or in the whole sprite - * sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and - * the images have not been fully loaded. - * @method getNumFrames - * @param {String} animation The name of the animation to get a frame count for. - * @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted. - */ - p.getNumFrames = function(animation) { - if (animation == null) { - return this._frames ? this._frames.length : this._numFrames || 0; - } else { - var data = this._data[animation]; - if (data == null) { return 0; } - else { return data.frames.length; } - } - }; - - /** - * Returns an object defining the specified animation. The returned object contains: - * @method getAnimation - * @param {String} name The name of the animation to get. - * @return {Object} a generic object with frames, speed, name, and next properties. - **/ - p.getAnimation = function(name) { - return this._data[name]; - }; - - /** - * Returns an object specifying the image and source rect of the specified frame. The returned object has: - * @method getFrame - * @param {Number} frameIndex The index of the frame. - * @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist. - **/ - p.getFrame = function(frameIndex) { - var frame; - if (this._frames && (frame=this._frames[frameIndex])) { return frame; } - return null; - }; - - /** - * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative - * to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return: - * - * [x=-50, y=-40, width=90, height=70] - * - * @method getFrameBounds - * @param {Number} frameIndex The index of the frame. - * @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created. - * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded. - **/ - p.getFrameBounds = function(frameIndex, rectangle) { - var frame = this.getFrame(frameIndex); - return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null; - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[SpriteSheet]"; - }; - - /** - * SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it. - * @method clone - **/ - p.clone = function() { - throw("SpriteSheet cannot be cloned.") - }; - -// private methods: - /** - * @method _parseData - * @param {Object} data An object describing the SpriteSheet data. - * @protected - **/ - p._parseData = function(data) { - var i,l,o,a; - if (data == null) { return; } - - this.framerate = data.framerate||0; - - // parse images: - if (data.images && (l=data.images.length) > 0) { - a = this._images = []; - for (i=0; i= maxFrames) { break imgLoop; } - frameCount++; - this._frames.push({ - image: img, - rect: new createjs.Rectangle(x, y, frameWidth, frameHeight), - regX: this._regX, - regY: this._regY - }); - x += frameWidth+spacing; - } - y += frameHeight+spacing; - } - } - this._numFrames = frameCount; - }; - - - createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher"); -}()); - -//############################################################################## -// Graphics.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a - * specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}} - * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the - * context of an EaselJS display list. - * - * There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or - * instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}. - * The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes. - * - * var g = new createjs.Graphics(); - * g.setStrokeStyle(1); - * g.beginStroke("#000000"); - * g.beginFill("red"); - * g.drawCircle(0,0,30); - * - * All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example, - * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill: - * - * myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50); - * - * Each graphics API call generates a command object (see below). The last command to be created can be accessed via - * {{#crossLink "Graphics/command:property"}}{{/crossLink}}: - * - * var fillCommand = myGraphics.beginFill("red").command; - * // ... later, update the fill style/color: - * fillCommand.style = "blue"; - * // or change it to a bitmap fill: - * fillCommand.bitmap(myImage); - * - * For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you - * need to manage path creation manually, and ensure that fill/stroke is applied to a defined path: - * - * // start a new path. Graphics.beginCmd is a reusable BeginPath instance: - * myGraphics.append(createjs.Graphics.beginCmd); - * // we need to define the path before applying the fill: - * var circle = new createjs.Graphics.Circle(0,0,30); - * myGraphics.append(circle); - * // fill the path we just defined: - * var fill = new createjs.Graphics.Fill("red"); - * myGraphics.append(fill); - * - * These approaches can be used together, for example to insert a custom command: - * - * myGraphics.beginFill("red"); - * var customCommand = new CustomSpiralCommand(etc); - * myGraphics.append(customCommand); - * myGraphics.beginFill("blue"); - * myGraphics.drawCircle(0, 0, 30); - * - * See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands. - * - *

Tiny API

- * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the - * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS - * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected - * descriptions in the docs. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
TinyMethodTinyMethod
mt{{#crossLink "Graphics/moveTo"}}{{/crossLink}} lt {{#crossLink "Graphics/lineTo"}}{{/crossLink}}
a/at{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} bt{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}}
qt{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)r{{#crossLink "Graphics/rect"}}{{/crossLink}}
cp{{#crossLink "Graphics/closePath"}}{{/crossLink}} c{{#crossLink "Graphics/clear"}}{{/crossLink}}
f{{#crossLink "Graphics/beginFill"}}{{/crossLink}} lf{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}}
rf{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} bf{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}}
ef{{#crossLink "Graphics/endFill"}}{{/crossLink}} ss / sd{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}}
s{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} ls{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}}
rs{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} bs{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}}
es{{#crossLink "Graphics/endStroke"}}{{/crossLink}} dr{{#crossLink "Graphics/drawRect"}}{{/crossLink}}
rr{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} rc{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}}
dc{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} de{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}}
dp{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} p{{#crossLink "Graphics/decodePath"}}{{/crossLink}}
- * - * Here is the above example, using the tiny API instead. - * - * myGraphics.s("red").f("blue").r(20, 20, 100, 50); - * - * @class Graphics - * @constructor - **/ - function Graphics() { - - - // public properties - /** - * Holds a reference to the last command that was created or appended. For example, you could retain a reference - * to a Fill command in order to dynamically update the color later by using: - * - * var myFill = myGraphics.beginFill("red").command; - * // update color later: - * myFill.style = "yellow"; - * - * @property command - * @type Object - **/ - this.command = null; - - - // private properties - /** - * @property _stroke - * @protected - * @type {Stroke} - **/ - this._stroke = null; - - /** - * @property _strokeStyle - * @protected - * @type {StrokeStyle} - **/ - this._strokeStyle = null; - - /** - * @property _oldStrokeStyle - * @protected - * @type {StrokeStyle} - **/ - this._oldStrokeStyle = null; - - /** - * @property _strokeDash - * @protected - * @type {StrokeDash} - **/ - this._strokeDash = null; - - /** - * @property _oldStrokeDash - * @protected - * @type {StrokeDash} - **/ - this._oldStrokeDash = null; - - /** - * @property _strokeIgnoreScale - * @protected - * @type Boolean - **/ - this._strokeIgnoreScale = false; - - /** - * @property _fill - * @protected - * @type {Fill} - **/ - this._fill = null; - - /** - * @property _instructions - * @protected - * @type {Array} - **/ - this._instructions = []; - - /** - * Indicates the last instruction index that was committed. - * @property _commitIndex - * @protected - * @type {Number} - **/ - this._commitIndex = 0; - - /** - * Uncommitted instructions. - * @property _activeInstructions - * @protected - * @type {Array} - **/ - this._activeInstructions = []; - - /** - * This indicates that there have been changes to the activeInstruction list since the last updateInstructions call. - * @property _dirty - * @protected - * @type {Boolean} - * @default false - **/ - this._dirty = false; - - /** - * Index to draw from if a store operation has happened. - * @property _storeIndex - * @protected - * @type {Number} - * @default 0 - **/ - this._storeIndex = 0; - - // setup: - this.clear(); - } - var p = Graphics.prototype; - var G = Graphics; // shortcut - -// static public methods: - /** - * Returns a CSS compatible color string based on the specified RGB numeric color values in the format - * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example, - * - * createjs.Graphics.getRGB(50, 100, 150, 0.5); - * // Returns "rgba(50,100,150,0.5)" - * - * It also supports passing a single hex color value as the first param, and an optional alpha value as the second - * param. For example, - * - * createjs.Graphics.getRGB(0xFF00FF, 0.2); - * // Returns "rgba(255,0,255,0.2)" - * - * @method getRGB - * @static - * @param {Number} r The red component for the color, between 0 and 0xFF (255). - * @param {Number} g The green component for the color, between 0 and 0xFF (255). - * @param {Number} b The blue component for the color, between 0 and 0xFF (255). - * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. - * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format - * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". - **/ - Graphics.getRGB = function(r, g, b, alpha) { - if (r != null && b == null) { - alpha = g; - b = r&0xFF; - g = r>>8&0xFF; - r = r>>16&0xFF; - } - if (alpha == null) { - return "rgb("+r+","+g+","+b+")"; - } else { - return "rgba("+r+","+g+","+b+","+alpha+")"; - } - }; - - /** - * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)", - * or if alpha is null then in the format "hsl(360,100,100)". - * - * createjs.Graphics.getHSL(150, 100, 70); - * // Returns "hsl(150,100,70)" - * - * @method getHSL - * @static - * @param {Number} hue The hue component for the color, between 0 and 360. - * @param {Number} saturation The saturation component for the color, between 0 and 100. - * @param {Number} lightness The lightness component for the color, between 0 and 100. - * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. - * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format - * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)". - **/ - Graphics.getHSL = function(hue, saturation, lightness, alpha) { - if (alpha == null) { - return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)"; - } else { - return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")"; - } - }; - - -// static properties: - /** - * A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid - * unnecessary instantiation. - * @property beginCmd - * @type {Graphics.BeginPath} - * @static - **/ - // defined at the bottom of this file. - - /** - * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}. - * @property BASE_64 - * @static - * @final - * @readonly - * @type {Object} - **/ - Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63}; - - /** - * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to - * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to - * "butt", 1 to "round", and 2 to "square". - * For example, to set the line caps to "square": - * - * myGraphics.ss(16, 2); - * - * @property STROKE_CAPS_MAP - * @static - * @final - * @readonly - * @type {Array} - **/ - Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"]; - - /** - * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to - * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to - * "miter", 1 to "round", and 2 to "bevel". - * For example, to set the line joints to "bevel": - * - * myGraphics.ss(16, 0, 2); - * - * @property STROKE_JOINTS_MAP - * @static - * @final - * @readonly - * @type {Array} - **/ - Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"]; - - /** - * @property _ctx - * @static - * @protected - * @type {CanvasRenderingContext2D} - **/ - var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); - if (canvas.getContext) { - Graphics._ctx = canvas.getContext("2d"); - canvas.width = canvas.height = 1; - } - - -// getter / setters: - /** - * Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead. - * @method _getInstructions - * @protected - * @return {Array} The instructions array, useful for chaining - **/ - p._getInstructions = function() { - this._updateInstructions(); - return this._instructions; - }; - // Graphics.getInstructions is @deprecated. Remove for 1.1+ - p.getInstructions = createjs.deprecate(p._getInstructions, "Graphics.getInstructions"); - - /** - * Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect) - * Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour. - * - * This property is mainly intended for introspection of the instructions (ex. for graphics export). - * @property instructions - * @type {Array} - * @readonly - **/ - try { - Object.defineProperties(p, { - instructions: { get: p._getInstructions } - }); - } catch (e) {} - - -// public methods: - /** - * Returns true if this Graphics instance has no drawing commands. - * @method isEmpty - * @return {Boolean} Returns true if this Graphics instance has no drawing commands. - **/ - p.isEmpty = function() { - return !(this._instructions.length || this._activeInstructions.length); - }; - - /** - * Draws the display object 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 {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data. - **/ - p.draw = function(ctx, data) { - this._updateInstructions(); - var instr = this._instructions; - for (var i=this._storeIndex, l=instr.length; iDisplayObject.mask to draw the clipping path, for example. - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method drawAsPath - * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into. - **/ - p.drawAsPath = function(ctx) { - this._updateInstructions(); - var instr, instrs = this._instructions; - for (var i=this._storeIndex, l=instrs.length; i - * whatwg spec. - * @method lineTo - * @param {Number} x The x coordinate the drawing point should draw to. - * @param {Number} y The y coordinate the drawing point should draw to. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.lineTo = function(x, y) { - return this.append(new G.LineTo(x,y)); - }; - - /** - * Draws an arc with the specified control points and radius. For detailed information, read the - * - * whatwg spec. A tiny API method "at" also exists. - * @method arcTo - * @param {Number} x1 - * @param {Number} y1 - * @param {Number} x2 - * @param {Number} y2 - * @param {Number} radius - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.arcTo = function(x1, y1, x2, y2, radius) { - return this.append(new G.ArcTo(x1, y1, x2, y2, radius)); - }; - - /** - * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For - * example, to draw a full circle with a radius of 20 centered at (100, 100): - * - * arc(100, 100, 20, 0, Math.PI*2); - * - * For detailed information, read the - * whatwg spec. - * A tiny API method "a" also exists. - * @method arc - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} startAngle Measured in radians. - * @param {Number} endAngle Measured in radians. - * @param {Boolean} anticlockwise - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) { - return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise)); - }; - - /** - * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed - * information, read the - * whatwg spec. A tiny API method "qt" also exists. - * @method quadraticCurveTo - * @param {Number} cpx - * @param {Number} cpy - * @param {Number} x - * @param {Number} y - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.quadraticCurveTo = function(cpx, cpy, x, y) { - return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y)); - }; - - /** - * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x, - * cp2y). For detailed information, read the - * - * whatwg spec. A tiny API method "bt" also exists. - * @method bezierCurveTo - * @param {Number} cp1x - * @param {Number} cp1y - * @param {Number} cp2x - * @param {Number} cp2y - * @param {Number} x - * @param {Number} y - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { - return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)); - }; - - /** - * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke. - * For detailed information, read the - * - * whatwg spec. A tiny API method "r" also exists. - * @method rect - * @param {Number} x - * @param {Number} y - * @param {Number} w Width of the rectangle - * @param {Number} h Height of the rectangle - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.rect = function(x, y, w, h) { - return this.append(new G.Rect(x, y, w, h)); - }; - - /** - * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified - * since the fill or stroke was last set. A tiny API method "cp" also exists. - * @method closePath - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.closePath = function() { - return this._activeInstructions.length ? this.append(new G.ClosePath()) : this; - }; - - -// public methods that roughly map to Adobe Flash/Animate graphics APIs: - /** - * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need - * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists. - * @method clear - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.clear = function() { - this._instructions.length = this._activeInstructions.length = this._commitIndex = 0; - this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null; - this._dirty = this._strokeIgnoreScale = false; - return this; - }; - - /** - * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists. - * @method beginFill - * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to - * null will result in no fill. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginFill = function(color) { - return this._setFill(color ? new G.Fill(color) : null); - }; - - /** - * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For - * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a - * square to display it: - * - * myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); - * - * A tiny API method "lf" also exists. - * @method beginLinearGradientFill - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient - * drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw - * the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. - * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) { - return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1)); - }; - - /** - * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to - * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it: - * - * myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50); - * - * A tiny API method "rf" also exists. - * @method beginRadialGradientFill - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 Center position of the inner circle that defines the gradient. - * @param {Number} y0 Center position of the inner circle that defines the gradient. - * @param {Number} r0 Radius of the inner circle that defines the gradient. - * @param {Number} x1 Center position of the outer circle that defines the gradient. - * @param {Number} y1 Center position of the outer circle that defines the gradient. - * @param {Number} r1 Radius of the outer circle that defines the gradient. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) { - return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1)); - }; - - /** - * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also - * exists. - * @method beginBitmapFill - * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use - * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty. - * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat", - * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or - * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat". - * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation - * will be applied relative to the parent transform. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginBitmapFill = function(image, repetition, matrix) { - return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition)); - }; - - /** - * Ends the current sub-path, and begins a new one with no fill. Functionally identical to beginFill(null). - * A tiny API method "ef" also exists. - * @method endFill - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.endFill = function() { - return this.beginFill(); - }; - - /** - * Sets the stroke style. Like all drawing methods, this can be chained, so you can define - * the stroke style and color in a single line of code like so: - * - * myGraphics.setStrokeStyle(8,"round").beginStroke("#F00"); - * - * A tiny API method "ss" also exists. - * @method setStrokeStyle - * @param {Number} thickness The width of the stroke. - * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt, - * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with - * the tiny API. - * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet. - * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) - * for use with the tiny API. - * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which - * controls at what point a mitered joint will be clipped. - * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless - * of active transformations. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) { - this._updateInstructions(true); - this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale); - - // ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery: - if (this._stroke) { this._stroke.ignoreScale = ignoreScale; } - this._strokeIgnoreScale = ignoreScale; - return this; - }; - - /** - * Sets or clears the stroke dash pattern. - * - * myGraphics.setStrokeDash([20, 10], 0); - * - * A tiny API method `sd` also exists. - * @method setStrokeDash - * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap. - * For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them. - * Passing null or an empty array will clear the existing stroke dash. - * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.setStrokeDash = function(segments, offset) { - this._updateInstructions(true); - this._strokeDash = this.command = new G.StrokeDash(segments, offset); - return this; - }; - - /** - * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists. - * @method beginStroke - * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to - * null will result in no stroke. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginStroke = function(color) { - return this._setStroke(color ? new G.Stroke(color) : null); - }; - - /** - * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For - * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a - * square to display it: - * - * myGraphics.setStrokeStyle(10). - * beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); - * - * A tiny API method "ls" also exists. - * @method beginLinearGradientStroke - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. - * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) { - return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1)); - }; - - /** - * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to - * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it: - * - * myGraphics.setStrokeStyle(10) - * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50) - * .drawRect(50, 90, 150, 110); - * - * A tiny API method "rs" also exists. - * @method beginRadialGradientStroke - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color - * to 100%. - * @param {Number} x0 Center position of the inner circle that defines the gradient. - * @param {Number} y0 Center position of the inner circle that defines the gradient. - * @param {Number} r0 Radius of the inner circle that defines the gradient. - * @param {Number} x1 Center position of the outer circle that defines the gradient. - * @param {Number} y1 Center position of the outer circle that defines the gradient. - * @param {Number} r1 Radius of the outer circle that defines the gradient. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) { - return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1)); - }; - - /** - * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills, - * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs" - * also exists. - * @method beginBitmapStroke - * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use - * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty. - * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of - * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.beginBitmapStroke = function(image, repetition) { - // NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width. - return this._setStroke(new G.Stroke().bitmap(image, repetition)); - }; - - /** - * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to beginStroke(null). - * A tiny API method "es" also exists. - * @method endStroke - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.endStroke = function() { - return this.beginStroke(); - }; - - /** - * Maps the familiar ActionScript curveTo() method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} - * method. - * @method curveTo - * @param {Number} cpx - * @param {Number} cpy - * @param {Number} x - * @param {Number} y - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.curveTo = p.quadraticCurveTo; - - /** - * - * Maps the familiar ActionScript drawRect() method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}} - * method. - * @method drawRect - * @param {Number} x - * @param {Number} y - * @param {Number} w Width of the rectangle - * @param {Number} h Height of the rectangle - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawRect = p.rect; - - /** - * Draws a rounded rectangle with all corners with the specified radius. - * @method drawRoundRect - * @param {Number} x - * @param {Number} y - * @param {Number} w - * @param {Number} h - * @param {Number} radius Corner radius. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawRoundRect = function(x, y, w, h, radius) { - return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius); - }; - - /** - * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API - * method "rc" also exists. - * @method drawRoundRectComplex - * @param {Number} x The horizontal coordinate to draw the round rect. - * @param {Number} y The vertical coordinate to draw the round rect. - * @param {Number} w The width of the round rect. - * @param {Number} h The height of the round rect. - * @param {Number} radiusTL Top left corner radius. - * @param {Number} radiusTR Top right corner radius. - * @param {Number} radiusBR Bottom right corner radius. - * @param {Number} radiusBL Bottom left corner radius. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) { - return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL)); - }; - - /** - * Draws a circle with the specified radius at (x, y). - * - * var g = new createjs.Graphics(); - * g.setStrokeStyle(1); - * g.beginStroke(createjs.Graphics.getRGB(0,0,0)); - * g.beginFill(createjs.Graphics.getRGB(255,0,0)); - * g.drawCircle(0,0,3); - * - * var s = new createjs.Shape(g); - * s.x = 100; - * s.y = 100; - * - * stage.addChild(s); - * stage.update(); - * - * A tiny API method "dc" also exists. - * @method drawCircle - * @param {Number} x x coordinate center point of circle. - * @param {Number} y y coordinate center point of circle. - * @param {Number} radius Radius of circle. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawCircle = function(x, y, radius) { - return this.append(new G.Circle(x, y, radius)); - }; - - /** - * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}, - * except the width and height can be different. A tiny API method "de" also exists. - * @method drawEllipse - * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} - * which draws from center. - * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} - * which draws from the center. - * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this - * number. - * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawEllipse = function(x, y, w, h) { - return this.append(new G.Ellipse(x, y, w, h)); - }; - - /** - * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of - * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a - * radius of 50: - * - * myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90); - * // Note: -90 makes the first point vertical - * - * A tiny API method "dp" also exists. - * - * @method drawPolyStar - * @param {Number} x Position of the center of the shape. - * @param {Number} y Position of the center of the shape. - * @param {Number} radius The outer radius of the shape. - * @param {Number} sides The number of points on the star or sides on the polygon. - * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular - * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy. - * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point - * directly to the right of the center. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) { - return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle)); - }; - - /** - * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method - * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into - * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw. - * - * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert - * built-in or custom graphics commands. For example: - * - * // attach data to our shape, so we can access it during the draw: - * myShape.color = "red"; - * - * // append a Circle command object: - * myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30)); - * - * // append a custom command object with an exec method that sets the fill style - * // based on the shape's data, and then fills the circle. - * myShape.graphics.append({exec:function(ctx, shape) { - * ctx.fillStyle = shape.color; - * ctx.fill(); - * }}); - * - * @method append - * @param {Object} command A graphics command object exposing an "exec" method. - * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.append = function(command, clean) { - this._activeInstructions.push(command); - this.command = command; - if (!clean) { this._dirty = true; } - return this; - }; - - /** - * Decodes a compact encoded path string into a series of draw instructions. - * This format is not intended to be human readable, and is meant for use by authoring tools. - * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw - * commands. - * - * Each command is comprised of a single "header" character followed by a variable number of alternating x and y - * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the - * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4 - * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the - * latter. Bits 5 and 6 are currently unused. - * - * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo) - * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the - * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed - * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the - * case of move operations which are absolute, this value is a delta from the previous x or y position (as - * appropriate). - * - * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0. - *
A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per - * parameter. - *
n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits - * indicate 1500 tenths of a pixel. - *
AA - 000000000000. Absolute y position of 0. - *
I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter. - *
Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to - * provide an absolute position of +150.0px. - *
AAA - 000000000000000000. A y delta value of 0. - * - * A tiny API method "p" also exists. - * @method decodePath - * @param {String} str The path string to decode. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.decodePath = function(str) { - var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath]; - var paramCount = [2, 2, 4, 6, 0]; - var i=0, l=str.length; - var params = []; - var x=0, y=0; - var base64 = Graphics.BASE_64; - - while (i>3; // highest order bits 1-3 code for operation. - var f = instructions[fi]; - // check that we have a valid instruction & that the unused bits are empty: - if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); } - var pl = paramCount[fi]; - if (!fi) { x=y=0; } // move operations reset the position. - params.length = 0; - i++; - var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation. - for (var p=0; p>5) ? -1 : 1; - num = ((num&31)<<6)|(base64[str.charAt(i+1)]); - if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); } - num = sign*num/10; - if (p%2) { x = (num += x); } - else { y = (num += y); } - params[p] = num; - i += charCount; - } - f.apply(this,params); - } - return this; - }; - - /** - * Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to - * the existing store. This also affects `drawAsPath()`. - * - * This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so - * that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all - * of the vector instructions for later use (ex. scaling, modifying, or exporting). - * - * Note that calling store() will force the active path (if any) to be ended in a manner similar to changing - * the fill or stroke. - * - * For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of - * segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}, - * and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG. - * - * // set up cache: - * myShape.cache(0,0,500,500,scale); - * - * // when the user drags, draw a new line: - * myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY); - * // then draw it into the existing cache: - * myShape.updateCache("source-over"); - * // store the new line, so it isn't redrawn next time: - * myShape.store(); - * - * // then, when the window resizes, we can re-render at a different scale: - * // first, unstore all our lines: - * myShape.unstore(); - * // then cache using the new scale: - * myShape.cache(0,0,500,500,newScale); - * // finally, store the existing commands again: - * myShape.store(); - * - * @method store - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.store = function() { - this._updateInstructions(true); - this._storeIndex = this._instructions.length; - return this; - }; - - /** - * Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}} - * so that they will be executed in subsequent draw calls. - * - * @method unstore - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.unstore = function() { - this._storeIndex = 0; - return this; - }; - - /** - * Returns a clone of this Graphics instance. Note that the individual command objects are not cloned. - * @method clone - * @return {Graphics} A clone of the current Graphics instance. - **/ - p.clone = function() { - var o = new Graphics(); - o.command = this.command; - o._stroke = this._stroke; - o._strokeStyle = this._strokeStyle; - o._strokeDash = this._strokeDash; - o._strokeIgnoreScale = this._strokeIgnoreScale; - o._fill = this._fill; - o._instructions = this._instructions.slice(); - o._commitIndex = this._commitIndex; - o._activeInstructions = this._activeInstructions.slice(); - o._dirty = this._dirty; - o._storeIndex = this._storeIndex; - return o; - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Graphics]"; - }; - - -// tiny API: - /** - * Shortcut to moveTo. - * @method mt - * @param {Number} x The x coordinate the drawing point should move to. - * @param {Number} y The y coordinate the drawing point should move to. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls). - * @chainable - * @protected - **/ - p.mt = p.moveTo; - - /** - * Shortcut to lineTo. - * @method lt - * @param {Number} x The x coordinate the drawing point should draw to. - * @param {Number} y The y coordinate the drawing point should draw to. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.lt = p.lineTo; - - /** - * Shortcut to arcTo. - * @method at - * @param {Number} x1 - * @param {Number} y1 - * @param {Number} x2 - * @param {Number} y2 - * @param {Number} radius - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.at = p.arcTo; - - /** - * Shortcut to bezierCurveTo. - * @method bt - * @param {Number} cp1x - * @param {Number} cp1y - * @param {Number} cp2x - * @param {Number} cp2y - * @param {Number} x - * @param {Number} y - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.bt = p.bezierCurveTo; - - /** - * Shortcut to quadraticCurveTo / curveTo. - * @method qt - * @param {Number} cpx - * @param {Number} cpy - * @param {Number} x - * @param {Number} y - * @protected - * @chainable - **/ - p.qt = p.quadraticCurveTo; - - /** - * Shortcut to arc. - * @method a - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} startAngle Measured in radians. - * @param {Number} endAngle Measured in radians. - * @param {Boolean} anticlockwise - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @protected - * @chainable - **/ - p.a = p.arc; - - /** - * Shortcut to rect. - * @method r - * @param {Number} x - * @param {Number} y - * @param {Number} w Width of the rectangle - * @param {Number} h Height of the rectangle - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.r = p.rect; - - /** - * Shortcut to closePath. - * @method cp - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.cp = p.closePath; - - /** - * Shortcut to clear. - * @method c - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.c = p.clear; - - /** - * Shortcut to beginFill. - * @method f - * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to - * null will result in no fill. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.f = p.beginFill; - - /** - * Shortcut to beginLinearGradientFill. - * @method lf - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient - * drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw - * the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. - * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.lf = p.beginLinearGradientFill; - - /** - * Shortcut to beginRadialGradientFill. - * @method rf - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 Center position of the inner circle that defines the gradient. - * @param {Number} y0 Center position of the inner circle that defines the gradient. - * @param {Number} r0 Radius of the inner circle that defines the gradient. - * @param {Number} x1 Center position of the outer circle that defines the gradient. - * @param {Number} y1 Center position of the outer circle that defines the gradient. - * @param {Number} r1 Radius of the outer circle that defines the gradient. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.rf = p.beginRadialGradientFill; - - /** - * Shortcut to beginBitmapFill. - * @method bf - * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use - * as the pattern. - * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat", - * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or - * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat". - * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation - * will be applied relative to the parent transform. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.bf = p.beginBitmapFill; - - /** - * Shortcut to endFill. - * @method ef - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.ef = p.endFill; - - /** - * Shortcut to setStrokeStyle. - * @method ss - * @param {Number} thickness The width of the stroke. - * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt, - * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with - * the tiny API. - * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet. - * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) - * for use with the tiny API. - * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which - * controls at what point a mitered joint will be clipped. - * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless - * of active transformations. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.ss = p.setStrokeStyle; - - /** - * Shortcut to setStrokeDash. - * @method sd - * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap. - * For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them. - * Passing null or an empty array will clear any existing dash. - * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.sd = p.setStrokeDash; - - /** - * Shortcut to beginStroke. - * @method s - * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to - * null will result in no stroke. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.s = p.beginStroke; - - /** - * Shortcut to beginLinearGradientStroke. - * @method ls - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. - * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. - * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. - * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.ls = p.beginLinearGradientStroke; - - /** - * Shortcut to beginRadialGradientStroke. - * @method rs - * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define - * a gradient drawing from red to blue. - * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, - * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color - * to 100%. - * @param {Number} x0 Center position of the inner circle that defines the gradient. - * @param {Number} y0 Center position of the inner circle that defines the gradient. - * @param {Number} r0 Radius of the inner circle that defines the gradient. - * @param {Number} x1 Center position of the outer circle that defines the gradient. - * @param {Number} y1 Center position of the outer circle that defines the gradient. - * @param {Number} r1 Radius of the outer circle that defines the gradient. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.rs = p.beginRadialGradientStroke; - - /** - * Shortcut to beginBitmapStroke. - * @method bs - * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use - * as the pattern. - * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of - * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.bs = p.beginBitmapStroke; - - /** - * Shortcut to endStroke. - * @method es - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.es = p.endStroke; - - /** - * Shortcut to drawRect. - * @method dr - * @param {Number} x - * @param {Number} y - * @param {Number} w Width of the rectangle - * @param {Number} h Height of the rectangle - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.dr = p.drawRect; - - /** - * Shortcut to drawRoundRect. - * @method rr - * @param {Number} x - * @param {Number} y - * @param {Number} w - * @param {Number} h - * @param {Number} radius Corner radius. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.rr = p.drawRoundRect; - - /** - * Shortcut to drawRoundRectComplex. - * @method rc - * @param {Number} x The horizontal coordinate to draw the round rect. - * @param {Number} y The vertical coordinate to draw the round rect. - * @param {Number} w The width of the round rect. - * @param {Number} h The height of the round rect. - * @param {Number} radiusTL Top left corner radius. - * @param {Number} radiusTR Top right corner radius. - * @param {Number} radiusBR Bottom right corner radius. - * @param {Number} radiusBL Bottom left corner radius. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.rc = p.drawRoundRectComplex; - - /** - * Shortcut to drawCircle. - * @method dc - * @param {Number} x x coordinate center point of circle. - * @param {Number} y y coordinate center point of circle. - * @param {Number} radius Radius of circle. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.dc = p.drawCircle; - - /** - * Shortcut to drawEllipse. - * @method de - * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} - * which draws from center. - * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} - * which draws from the center. - * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this - * number. - * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.de = p.drawEllipse; - - /** - * Shortcut to drawPolyStar. - * @method dp - * @param {Number} x Position of the center of the shape. - * @param {Number} y Position of the center of the shape. - * @param {Number} radius The outer radius of the shape. - * @param {Number} sides The number of points on the star or sides on the polygon. - * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular - * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy. - * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point - * directly to the right of the center. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.dp = p.drawPolyStar; - - /** - * Shortcut to decodePath. - * @method p - * @param {String} str The path string to decode. - * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) - * @chainable - * @protected - **/ - p.p = p.decodePath; - - -// private methods: - /** - * @method _updateInstructions - * @param commit - * @protected - **/ - p._updateInstructions = function(commit) { - var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex; - - if (this._dirty && active.length) { - instr.length = commitIndex; // remove old, uncommitted commands - instr.push(Graphics.beginCmd); - - var l = active.length, ll = instr.length; - instr.length = ll+l; - for (var i=0; i= 2) { - var o = this.style = Graphics._ctx.createPattern(image, repetition || ""); - o.props = {image: image, repetition: repetition, type: "bitmap"}; - } - return this; - }; - p.path = false; - - /** - * Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class Stroke - * @constructor - * @param {Object} style A valid Context2D fillStyle. - * @param {Boolean} ignoreScale - **/ - /** - * A valid Context2D strokeStyle. - * @property style - * @type Object - */ - /** - * @property ignoreScale - * @type Boolean - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - p = (G.Stroke = function(style, ignoreScale) { - this.style = style; - this.ignoreScale = ignoreScale; - }).prototype; - p.exec = function(ctx) { - if (!this.style) { return; } - ctx.strokeStyle = this.style; - if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); } - ctx.stroke(); - if (this.ignoreScale) { ctx.restore(); } - }; - /** - * Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. - * See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information. - * @method linearGradient - * @param {Array} colors - * @param {Array} ratios - * @param {Number} x0 - * @param {Number} y0 - * @param {Number} x1 - * @param {Number} y1 - * @return {Fill} Returns this Stroke object for chaining or assignment. - */ - p.linearGradient = G.Fill.prototype.linearGradient; - /** - * Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. - * See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information. - * @method radialGradient - * @param {Array} colors - * @param {Array} ratios - * @param {Number} x0 - * @param {Number} y0 - * @param {Number} r0 - * @param {Number} x1 - * @param {Number} y1 - * @param {Number} r1 - * @return {Fill} Returns this Stroke object for chaining or assignment. - */ - p.radialGradient = G.Fill.prototype.radialGradient; - /** - * Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. - * See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information. - * @method bitmap - * @param {HTMLImageElement} image - * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat. - * @return {Fill} Returns this Stroke object for chaining or assignment. - */ - p.bitmap = G.Fill.prototype.bitmap; - p.path = false; - - /** - * Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class StrokeStyle - * @constructor - * @param {Number} width - * @param {String} [caps=butt] - * @param {String} [joints=miter] - * @param {Number} [miterLimit=10] - * @param {Boolean} [ignoreScale=false] - **/ - /** - * @property width - * @type Number - */ - /** - * One of: butt, round, square - * @property caps - * @type String - */ - /** - * One of: round, bevel, miter - * @property joints - * @type String - */ - /** - * @property miterLimit - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - p = (G.StrokeStyle = function(width, caps, joints, miterLimit, ignoreScale) { - this.width = width; - this.caps = caps; - this.joints = joints; - this.miterLimit = miterLimit; - this.ignoreScale = ignoreScale; - }).prototype; - p.exec = function(ctx) { - ctx.lineWidth = (this.width == null ? "1" : this.width); - ctx.lineCap = (this.caps == null ? "butt" : (isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps])); - ctx.lineJoin = (this.joints == null ? "miter" : (isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints])); - ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit); - ctx.ignoreScale = (this.ignoreScale == null ? false : this.ignoreScale); - }; - p.path = false; - - /** - * Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class StrokeDash - * @constructor - * @param {Array} [segments] - * @param {Number} [offset=0] - **/ - /** - * @property segments - * @type Array - */ - /** - * @property offset - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - (G.StrokeDash = function(segments, offset) { - this.segments = segments; - this.offset = offset||0; - }).prototype.exec = function(ctx) { - if (ctx.setLineDash) { // feature detection. - ctx.setLineDash(this.segments|| G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn. - ctx.lineDashOffset = this.offset||0; - } - }; - /** - * The default value for segments (ie. no dash). - * @property EMPTY_SEGMENTS - * @static - * @final - * @readonly - * @protected - * @type {Array} - **/ - G.StrokeDash.EMPTY_SEGMENTS = []; - - /** - * Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class RoundRect - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} w - * @param {Number} h - * @param {Number} radiusTL - * @param {Number} radiusTR - * @param {Number} radiusBR - * @param {Number} radiusBL - **/ - /** - * @property x - * @type Number - */ - /** - * @property y - * @type Number - */ - /** - * @property w - * @type Number - */ - /** - * @property h - * @type Number - */ - /** - * @property radiusTL - * @type Number - */ - /** - * @property radiusTR - * @type Number - */ - /** - * @property radiusBR - * @type Number - */ - /** - * @property radiusBL - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - (G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) { - this.x = x; this.y = y; - this.w = w; this.h = h; - this.radiusTL = radiusTL; this.radiusTR = radiusTR; - this.radiusBR = radiusBR; this.radiusBL = radiusBL; - }).prototype.exec = function(ctx) { - var max = (w max) { rTL = max; } - if (rTR < 0) { rTR *= (mTR=-1); } - if (rTR > max) { rTR = max; } - if (rBR < 0) { rBR *= (mBR=-1); } - if (rBR > max) { rBR = max; } - if (rBL < 0) { rBL *= (mBL=-1); } - if (rBL > max) { rBL = max; } - - ctx.moveTo(x+w-rTR, y); - ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR); - ctx.lineTo(x+w, y+h-rBR); - ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR); - ctx.lineTo(x+rBL, y+h); - ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL); - ctx.lineTo(x, y+rTL); - ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL); - ctx.closePath(); - }; - - /** - * Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class Circle - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} radius - **/ - /** - * @property x - * @type Number - */ - /** - * @property y - * @type Number - */ - /** - * @property radius - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - (G.Circle = function(x, y, radius) { - this.x = x; this.y = y; - this.radius = radius; - }).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); }; - - /** - * Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class Ellipse - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} w - * @param {Number} h - **/ - /** - * @property x - * @type Number - */ - /** - * @property y - * @type Number - */ - /** - * @property w - * @type Number - */ - /** - * @property h - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - (G.Ellipse = function(x, y, w, h) { - this.x = x; this.y = y; - this.w = w; this.h = h; - }).prototype.exec = function(ctx) { - var x = this.x, y = this.y; - var w = this.w, h = this.h; - - var k = 0.5522848; - var ox = (w / 2) * k; - var oy = (h / 2) * k; - var xe = x + w; - var ye = y + h; - var xm = x + w / 2; - var ym = y + h / 2; - - ctx.moveTo(x, ym); - ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y); - ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym); - ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye); - ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym); - }; - - /** - * Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. - * @class PolyStar - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @param {Number} sides - * @param {Number} pointSize - * @param {Number} angle - **/ - /** - * @property x - * @type Number - */ - /** - * @property y - * @type Number - */ - /** - * @property radius - * @type Number - */ - /** - * @property sides - * @type Number - */ - /** - * @property pointSize - * @type Number - */ - /** - * @property angle - * @type Number - */ - /** - * Execute the Graphics command in the provided Canvas context. - * @method exec - * @param {CanvasRenderingContext2D} ctx The canvas rendering context - */ - (G.PolyStar = function(x, y, radius, sides, pointSize, angle) { - this.x = x; this.y = y; - this.radius = radius; - this.sides = sides; - this.pointSize = pointSize; - this.angle = angle; - }).prototype.exec = function(ctx) { - var x = this.x, y = this.y; - var radius = this.radius; - var angle = (this.angle||0)/180*Math.PI; - var sides = this.sides; - var ps = 1-(this.pointSize||0); - var a = Math.PI/sides; - - ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius); - for (var i=0; iExample + * + * createjs.Ticker.addEventListener("tick", handleTick); + * function handleTick(event) { + * // Actions carried out each tick (aka frame) + * if (!event.paused) { + * // Actions carried out when the Ticker is not paused. + * } + * } + * + * @class Ticker + * @uses EventDispatcher + * @static **/ - function DisplayObject() { - this.EventDispatcher_constructor(); + function Ticker() { + throw "Ticker cannot be instantiated."; + } - // public properties: - /** - * The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque. - * @property alpha - * @type {Number} - * @default 1 - **/ - this.alpha = 1; +// constants: + /** + * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It + * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and + * dispatches the tick when the time is within a certain threshold. + * + * This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, + * but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while + * gaining the benefits of that API (screen synch, background throttling). + * + * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so + * framerates of 10, 12, 15, 20, and 30 work well. + * + * Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not + * supported. + * @property RAF_SYNCHED + * @static + * @type {String} + * @default "synched" + * @readonly + **/ + Ticker.RAF_SYNCHED = "synched"; - /** - * 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} - * @default null - * @readonly - **/ - this.cacheCanvas = null; + /** + * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely. + * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based. + * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}} + * event object's "delta" properties to make this easier. + * + * Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not + * supported. + * @property RAF + * @static + * @type {String} + * @default "raf" + * @readonly + **/ + Ticker.RAF = "raf"; - /** - * If a cache has been made, this returns the class that is managing the cacheCanvas and its properties. See {{#crossLink "BitmapCache"}}{{/crossLink}} - * for more information. Use this to control, inspect, and change the cache. In special circumstances this may be a modified or subclassed BitmapCache. - * @property bitmapCache - * @type {BitmapCache} - * @default null - * @readonly - **/ - this.bitmapCache = null; + /** + * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not + * provide the benefits of requestAnimationFrame (screen synch, background throttling). + * @property TIMEOUT + * @static + * @type {String} + * @default "timeout" + * @readonly + **/ + Ticker.TIMEOUT = "timeout"; - /** - * Unique ID for this display object. Makes display objects easier for some uses. - * @property id + +// static events: + /** + * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using + * {{#crossLink "Ticker/paused:property"}}{{/crossLink}}. + * + *

Example

+ * + * createjs.Ticker.addEventListener("tick", handleTick); + * function handleTick(event) { + * console.log("Paused:", event.paused, event.delta); + * } + * + * @event tick + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @param {Boolean} paused Indicates whether the ticker is currently paused. + * @param {Number} delta The time elapsed in ms since the last tick. + * @param {Number} time The total time in ms since Ticker was initialized. + * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example, + * you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`. + * @since 0.6.0 + */ + + +// public static properties: + /** + * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See + * {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, {{#crossLink "Ticker/RAF:property"}}{{/crossLink}}, and + * {{#crossLink "Ticker/RAF_SYNCHED:property"}}{{/crossLink}} for mode details. + * @property timingMode + * @static + * @type {String} + * @default Ticker.TIMEOUT + **/ + Ticker.timingMode = null; + + /** + * Specifies a maximum value for the delta property in the tick event object. This is useful when building time + * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep, + * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value + * (ex. maxDelta=50 when running at 40fps). + * + * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta + * when using both delta and other values. + * + * If 0, there is no maximum. + * @property maxDelta + * @static + * @type {number} + * @default 0 + */ + Ticker.maxDelta = 0; + + /** + * When the ticker is paused, all listeners will still receive a tick event, but the paused property + * of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}}, + * {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more + * info. + * + *

Example

+ * + * createjs.Ticker.addEventListener("tick", handleTick); + * createjs.Ticker.paused = true; + * function handleTick(event) { + * console.log(event.paused, + * createjs.Ticker.getTime(false), + * createjs.Ticker.getTime(true)); + * } + * + * @property paused + * @static + * @type {Boolean} + * @default false + **/ + Ticker.paused = false; + + +// mix-ins: + // EventDispatcher methods: + Ticker.removeEventListener = null; + Ticker.removeAllEventListeners = null; + Ticker.dispatchEvent = null; + Ticker.hasEventListener = null; + Ticker._listeners = null; + createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods. + Ticker._addEventListener = Ticker.addEventListener; + Ticker.addEventListener = function() { + !Ticker._inited&&Ticker.init(); + return Ticker._addEventListener.apply(Ticker, arguments); + }; + + +// private static properties: + /** + * @property _inited + * @static + * @type {Boolean} + * @private + **/ + Ticker._inited = false; + + /** + * @property _startTime + * @static + * @type {Number} + * @private + **/ + Ticker._startTime = 0; + + /** + * @property _pausedTime + * @static + * @type {Number} + * @private + **/ + Ticker._pausedTime=0; + + /** + * The number of ticks that have passed + * @property _ticks + * @static + * @type {Number} + * @private + **/ + Ticker._ticks = 0; + + /** + * The number of ticks that have passed while Ticker has been paused + * @property _pausedTicks + * @static + * @type {Number} + * @private + **/ + Ticker._pausedTicks = 0; + + /** + * @property _interval + * @static + * @type {Number} + * @private + **/ + Ticker._interval = 50; + + /** + * @property _lastTime + * @static + * @type {Number} + * @private + **/ + Ticker._lastTime = 0; + + /** + * @property _times + * @static + * @type {Array} + * @private + **/ + Ticker._times = null; + + /** + * @property _tickTimes + * @static + * @type {Array} + * @private + **/ + Ticker._tickTimes = null; + + /** + * Stores the timeout or requestAnimationFrame id. + * @property _timerId + * @static + * @type {Number} + * @private + **/ + Ticker._timerId = null; + + /** + * True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode + * if that property changed and a tick hasn't fired. + * @property _raf + * @static + * @type {Boolean} + * @private + **/ + Ticker._raf = true; + + +// static getter / setters: + /** + * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead. + * @method _setInterval + * @private + * @static + * @param {Number} interval + **/ + Ticker._setInterval = function(interval) { + Ticker._interval = interval; + if (!Ticker._inited) { return; } + Ticker._setupTick(); + }; + // Ticker.setInterval is @deprecated. Remove for 1.1+ + Ticker.setInterval = createjs.deprecate(Ticker._setInterval, "Ticker.setInterval"); + + /** + * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead. + * @method _getInterval + * @private + * @static + * @return {Number} + **/ + Ticker._getInterval = function() { + return Ticker._interval; + }; + // Ticker.getInterval is @deprecated. Remove for 1.1+ + Ticker.getInterval = createjs.deprecate(Ticker._getInterval, "Ticker.getInterval"); + + /** + * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead. + * @method _setFPS + * @private + * @static + * @param {Number} value + **/ + Ticker._setFPS = function(value) { + Ticker._setInterval(1000/value); + }; + // Ticker.setFPS is @deprecated. Remove for 1.1+ + Ticker.setFPS = createjs.deprecate(Ticker._setFPS, "Ticker.setFPS"); + + /** + * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead. + * @method _getFPS + * @static + * @private + * @return {Number} + **/ + Ticker._getFPS = function() { + return 1000/Ticker._interval; + }; + // Ticker.getFPS is @deprecated. Remove for 1.1+ + Ticker.getFPS = createjs.deprecate(Ticker._getFPS, "Ticker.getFPS"); + + /** + * Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS). + * Note that actual time between ticks may be more than specified depending on CPU load. + * This property is ignored if the ticker is using the `RAF` timing mode. + * @property interval + * @static + * @type {Number} + **/ + + /** + * Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where + * `framerate == 1000/interval`. + * @property framerate + * @static + * @type {Number} + **/ + try { + Object.defineProperties(Ticker, { + interval: { get: Ticker._getInterval, set: Ticker._setInterval }, + framerate: { get: Ticker._getFPS, set: Ticker._setFPS } + }); + } catch (e) { console.log(e); } + + +// public static methods: + /** + * Starts the tick. This is called automatically when the first listener is added. + * @method init + * @static + **/ + Ticker.init = function() { + if (Ticker._inited) { return; } + Ticker._inited = true; + Ticker._times = []; + Ticker._tickTimes = []; + Ticker._startTime = Ticker._getTime(); + Ticker._times.push(Ticker._lastTime = 0); + Ticker.interval = Ticker._interval; + }; + + /** + * Stops the Ticker and removes all listeners. Use init() to restart the Ticker. + * @method reset + * @static + **/ + Ticker.reset = function() { + if (Ticker._raf) { + var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame; + f&&f(Ticker._timerId); + } else { + clearTimeout(Ticker._timerId); + } + Ticker.removeAllEventListeners("tick"); + Ticker._timerId = Ticker._times = Ticker._tickTimes = null; + Ticker._startTime = Ticker._lastTime = Ticker._ticks = Ticker._pausedTime = 0; + Ticker._inited = false; + }; + + /** + * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS + * because it only measures the time spent within the tick execution stack. + * + * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between + * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that + * there may be up to 35ms of "idle" time between the end of one tick and the start of the next. + * + * Example 2: With a target FPS of 30, {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} returns 10fps, which + * indicates an average of 100ms between the end of one tick and the end of the next. However, {{#crossLink "Ticker/getMeasuredTickTime"}}{{/crossLink}} + * returns 20ms. This would indicate that something other than the tick is using ~80ms (another script, DOM + * rendering, etc). + * @method getMeasuredTickTime + * @static + * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick. + * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1. + * @return {Number} The average time spent in a tick in milliseconds. + **/ + Ticker.getMeasuredTickTime = function(ticks) { + var ttl=0, times=Ticker._tickTimes; + if (!times || times.length < 1) { return -1; } + + // by default, calculate average for the past ~1 second: + ticks = Math.min(times.length, ticks||(Ticker._getFPS()|0)); + for (var i=0; i= (Ticker._interval-1)*0.97) { + Ticker._tick(); + } + }; + + /** + * @method _handleRAF + * @static + * @private + **/ + Ticker._handleRAF = function() { + Ticker._timerId = null; + Ticker._setupTick(); + Ticker._tick(); + }; + + /** + * @method _handleTimeout + * @static + * @private + **/ + Ticker._handleTimeout = function() { + Ticker._timerId = null; + Ticker._setupTick(); + Ticker._tick(); + }; + + /** + * @method _setupTick + * @static + * @private + **/ + Ticker._setupTick = function() { + if (Ticker._timerId != null) { return; } // avoid duplicates + + var mode = Ticker.timingMode; + if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) { + var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; + if (f) { + Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch); + Ticker._raf = true; + return; + } + } + Ticker._raf = false; + Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval); + }; + + /** + * @method _tick + * @static + * @private + **/ + Ticker._tick = function() { + var paused = Ticker.paused; + var time = Ticker._getTime(); + var elapsedTime = time-Ticker._lastTime; + Ticker._lastTime = time; + Ticker._ticks++; + + if (paused) { + Ticker._pausedTicks++; + Ticker._pausedTime += elapsedTime; + } + + if (Ticker.hasEventListener("tick")) { + var event = new createjs.Event("tick"); + var maxDelta = Ticker.maxDelta; + event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime; + event.paused = paused; + event.time = time; + event.runTime = time-Ticker._pausedTime; + Ticker.dispatchEvent(event); + } + + Ticker._tickTimes.unshift(Ticker._getTime()-time); + while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); } + + Ticker._times.unshift(time); + while (Ticker._times.length > 100) { Ticker._times.pop(); } + }; + + /** + * @method _getTime + * @static + * @private + **/ + var w=window, now=w.performance.now || w.performance.mozNow || w.performance.msNow || w.performance.oNow || w.performance.webkitNow; + Ticker._getTime = function() { + return ((now&&now.call(w.performance))||(new Date().getTime())) - Ticker._startTime; + }; + + + createjs.Ticker = Ticker; +}()); + +//############################################################################## +// VideoBuffer.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * When an HTML video seeks, including when looping, there is an indeterminate period before a new frame is available. + * This can result in the video blinking or flashing when it is drawn to a canvas. The VideoBuffer class resolves + * this issue by drawing each frame to an off-screen canvas and preserving the prior frame during a seek. + * + * var myBuffer = new createjs.VideoBuffer(myVideo); + * var myBitmap = new Bitmap(myBuffer); + * + * @class VideoBuffer + * @param {HTMLVideoElement} video The HTML video element to buffer. + * @constructor + **/ + function VideoBuffer(video) { + + // private properties: + /** + * Used by Bitmap to determine when the video buffer is ready to be drawn. Not intended for general use. + * @property readyState + * @protected + * @type {Number} + * @default 0 + **/ + this.readyState = video.readyState; + + /** + * @property _video + * @protected + * @type {HTMLVideoElement} + * @default 0 + **/ + this._video = video; + + /** + * @property _canvas + * @protected + * @type {HTMLCanvasElement} + * @default 0 + **/ + this._canvas = null; + + /** + * @property _lastTime + * @protected * @type {Number} * @default -1 **/ - this.id = createjs.UID.get(); + this._lastTime = -1; + + if (this.readyState < 2) { video.addEventListener("canplaythrough", this._videoReady.bind(this)); } //once:true isn't supported everywhere, but its a non-critical optimization here. + } + var p = VideoBuffer.prototype; + + +// public methods: + /** + * Gets an HTML canvas element showing the current video frame, or the previous frame if in a seek / loop. + * Primarily for use by {{#crossLink "Bitmap"}}{{/crossLink}}. + * @method getImage + **/ + p.getImage = function() { + if (this.readyState < 2) { return; } + var canvas=this._canvas, video = this._video; + if (!canvas) { + canvas = this._canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + } + if (video.readyState >= 2 && video.currentTime !== this._lastTime) { + var ctx = canvas.getContext("2d"); + ctx.clearRect(0,0,canvas.width,canvas.height); + ctx.drawImage(video,0,0,canvas.width,canvas.height); + this._lastTime = video.currentTime; + } + return canvas; + }; + +// private methods: + /** + * @method _videoReady + * @protected + **/ + p._videoReady = function() { + this.readyState = 2; + }; + + + createjs.VideoBuffer = VideoBuffer; +}()); + +//############################################################################## +// MouseEvent.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties, + * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings. + * @class MouseEvent + * @param {String} type The event type. + * @param {Boolean} bubbles Indicates whether the event will bubble through the display list. + * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled. + * @param {Number} stageX The normalized x position relative to the stage. + * @param {Number} stageY The normalized y position relative to the stage. + * @param {MouseEvent} nativeEvent The native DOM event related to this mouse event. + * @param {Number} pointerID The unique id for the pointer. + * @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment. + * @param {Number} rawX The raw x position relative to the stage. + * @param {Number} rawY The raw y position relative to the stage. + * @param {DisplayObject} relatedTarget The secondary target for the event. + * @extends Event + * @constructor + **/ + function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) { + this.Event_constructor(type, bubbles, cancelable); + + + // public properties: + /** + * The normalized x position on the stage. This will always be within the range 0 to stage width. + * @property stageX + * @type Number + */ + this.stageX = stageX; + + /** + * The normalized y position on the stage. This will always be within the range 0 to stage height. + * @property stageY + * @type Number + **/ + this.stageY = stageY; + + /** + * The raw x position relative to the stage. Normally this will be the same as the stageX value, unless + * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds. + * @property rawX + * @type Number + */ + this.rawX = (rawX==null)?stageX:rawX; + + /** + * The raw y position relative to the stage. Normally this will be the same as the stageY value, unless + * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds. + * @property rawY + * @type Number + */ + this.rawY = (rawY==null)?stageY:rawY; + + /** + * The native MouseEvent generated by the browser. The properties and API for this + * event may differ between browsers. This property will be null if the + * EaselJS property was not directly generated from a native MouseEvent. + * @property nativeEvent + * @type HtmlMouseEvent + * @default null + **/ + this.nativeEvent = nativeEvent; + + /** + * The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system + * supplied id value. + * @property pointerID + * @type {Number} + */ + this.pointerID = pointerID; + + /** + * Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse. + * For touch pointers, the first pointer in the current stack will be considered the primary pointer. + * @property primary + * @type {Boolean} + */ + this.primary = !!primary; + + /** + * The secondary target for the event, if applicable. This is used for mouseout/rollout + * events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited, + * and stagemousedown/stagemouseup events for the object that was the under the cursor, if any. + * + * Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set). + * @property relatedTarget + * @type {DisplayObject} + */ + this.relatedTarget = relatedTarget; + } + var p = createjs.extend(MouseEvent, createjs.Event); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. + + +// getter / setters: + /** + * Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher). + * @property localX + * @type {Number} + * @readonly + */ + p._get_localX = function() { + return this.currentTarget.globalToLocal(this.rawX, this.rawY).x; + }; + + /** + * Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher). + * @property localY + * @type {Number} + * @readonly + */ + p._get_localY = function() { + return this.currentTarget.globalToLocal(this.rawX, this.rawY).y; + }; + + /** + * Indicates whether the event was generated by a touch input (versus a mouse input). + * @property isTouch + * @type {Boolean} + * @readonly + */ + p._get_isTouch = function() { + return this.pointerID !== -1; + }; + + + try { + Object.defineProperties(p, { + localX: { get: p._get_localX }, + localY: { get: p._get_localY }, + isTouch: { get: p._get_isTouch } + }); + } catch (e) {} // TODO: use Log + + +// public methods: + /** + * Returns a clone of the MouseEvent instance. + * @method clone + * @return {MouseEvent} a clone of the MouseEvent instance. + **/ + p.clone = function() { + return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]"; + }; + + + createjs.MouseEvent = createjs.promote(MouseEvent, "Event"); +}()); + +//############################################################################## +// Matrix2D.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices. + * + * This matrix can be visualized as: + * + * [ a c tx + * b d ty + * 0 0 1 ] + * + * Note the locations of b and c. + * + * @class Matrix2D + * @param {Number} [a=1] Specifies the a property for the new matrix. + * @param {Number} [b=0] Specifies the b property for the new matrix. + * @param {Number} [c=0] Specifies the c property for the new matrix. + * @param {Number} [d=1] Specifies the d property for the new matrix. + * @param {Number} [tx=0] Specifies the tx property for the new matrix. + * @param {Number} [ty=0] Specifies the ty property for the new matrix. + * @constructor + **/ + function Matrix2D(a, b, c, d, tx, ty) { + this.setValues(a,b,c,d,tx,ty); + + // public properties: + // assigned in the setValues method. + /** + * Position (0, 0) in a 3x3 affine transformation matrix. + * @property a + * @type Number + **/ + + /** + * Position (0, 1) in a 3x3 affine transformation matrix. + * @property b + * @type Number + **/ + + /** + * Position (1, 0) in a 3x3 affine transformation matrix. + * @property c + * @type Number + **/ + + /** + * Position (1, 1) in a 3x3 affine transformation matrix. + * @property d + * @type Number + **/ + + /** + * Position (2, 0) in a 3x3 affine transformation matrix. + * @property tx + * @type Number + **/ + + /** + * Position (2, 1) in a 3x3 affine transformation matrix. + * @property ty + * @type Number + **/ + } + var p = Matrix2D.prototype; + +// constants: + /** + * Multiplier for converting degrees to radians. Used internally by Matrix2D. + * @property DEG_TO_RAD + * @static + * @final + * @type Number + * @readonly + **/ + Matrix2D.DEG_TO_RAD = Math.PI/180; + + +// static public properties: + /** + * An identity matrix, representing a null transformation. + * @property identity + * @static + * @type Matrix2D + * @readonly + **/ + Matrix2D.identity = null; // set at bottom of class definition. + + +// public methods: + /** + * Sets the specified values on this instance. + * @method setValues + * @param {Number} [a=1] Specifies the a property for the new matrix. + * @param {Number} [b=0] Specifies the b property for the new matrix. + * @param {Number} [c=0] Specifies the c property for the new matrix. + * @param {Number} [d=1] Specifies the d property for the new matrix. + * @param {Number} [tx=0] Specifies the tx property for the new matrix. + * @param {Number} [ty=0] Specifies the ty property for the new matrix. + * @return {Matrix2D} This instance. Useful for chaining method calls. + */ + p.setValues = function(a, b, c, d, tx, ty) { + // don't forget to update docs in the constructor if these change: + this.a = (a == null) ? 1 : a; + this.b = b || 0; + this.c = c || 0; + this.d = (d == null) ? 1 : d; + this.tx = tx || 0; + this.ty = ty || 0; + return this; + }; + + /** + * Appends the specified matrix properties to this matrix. All parameters are required. + * This is the equivalent of multiplying `(this matrix) * (specified matrix)`. + * @method append + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} tx + * @param {Number} ty + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.append = function(a, b, c, d, tx, ty) { + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + if (a != 1 || b != 0 || c != 0 || d != 1) { + this.a = a1*a+c1*b; + this.b = b1*a+d1*b; + this.c = a1*c+c1*d; + this.d = b1*c+d1*d; + } + this.tx = a1*tx+c1*ty+this.tx; + this.ty = b1*tx+d1*ty+this.ty; + return this; + }; + + /** + * Prepends the specified matrix properties to this matrix. + * This is the equivalent of multiplying `(specified matrix) * (this matrix)`. + * All parameters are required. + * @method prepend + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} tx + * @param {Number} ty + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.prepend = function(a, b, c, d, tx, ty) { + var a1 = this.a; + var c1 = this.c; + var tx1 = this.tx; + + this.a = a*a1+c*this.b; + this.b = b*a1+d*this.b; + this.c = a*c1+c*this.d; + this.d = b*c1+d*this.d; + this.tx = a*tx1+c*this.ty+tx; + this.ty = b*tx1+d*this.ty+ty; + return this; + }; + + /** + * Appends the specified matrix to this matrix. + * This is the equivalent of multiplying `(this matrix) * (specified matrix)`. + * @method appendMatrix + * @param {Matrix2D} matrix + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.appendMatrix = function(matrix) { + return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); + }; + + /** + * Prepends the specified matrix to this matrix. + * This is the equivalent of multiplying `(specified matrix) * (this matrix)`. + * For example, you could calculate the combined transformation for a child object using: + * + * var o = myDisplayObject; + * var mtx = o.getMatrix(); + * while (o = o.parent) { + * // prepend each parent's transformation in turn: + * o.prependMatrix(o.getMatrix()); + * } + * @method prependMatrix + * @param {Matrix2D} matrix + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.prependMatrix = function(matrix) { + return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); + }; + + /** + * Generates matrix properties from the specified display object transform properties, and appends them to this matrix. + * For example, you can use this to generate a matrix representing the transformations of a display object: + * + * var mtx = new createjs.Matrix2D(); + * mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation); + * @method appendTransform + * @param {Number} x + * @param {Number} y + * @param {Number} scaleX + * @param {Number} scaleY + * @param {Number} rotation + * @param {Number} skewX + * @param {Number} skewY + * @param {Number} regX Optional. + * @param {Number} regY Optional. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { + if (rotation%360) { + var r = rotation*Matrix2D.DEG_TO_RAD; + var cos = Math.cos(r); + var sin = Math.sin(r); + } else { + cos = 1; + sin = 0; + } + + if (skewX || skewY) { + // TODO: can this be combined into a single append operation? + skewX *= Matrix2D.DEG_TO_RAD; + skewY *= Matrix2D.DEG_TO_RAD; + this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); + this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); + } else { + this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); + } + + if (regX || regY) { + // append the registration offset: + this.tx -= regX*this.a+regY*this.c; + this.ty -= regX*this.b+regY*this.d; + } + return this; + }; + + /** + * Generates matrix properties from the specified display object transform properties, and prepends them to this matrix. + * For example, you could calculate the combined transformation for a child object using: + * + * var o = myDisplayObject; + * var mtx = new createjs.Matrix2D(); + * do { + * // prepend each parent's transformation in turn: + * mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY); + * } while (o = o.parent); + * + * Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}} + * values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does. + * @method prependTransform + * @param {Number} x + * @param {Number} y + * @param {Number} scaleX + * @param {Number} scaleY + * @param {Number} rotation + * @param {Number} skewX + * @param {Number} skewY + * @param {Number} regX Optional. + * @param {Number} regY Optional. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { + if (rotation%360) { + var r = rotation*Matrix2D.DEG_TO_RAD; + var cos = Math.cos(r); + var sin = Math.sin(r); + } else { + cos = 1; + sin = 0; + } + + if (regX || regY) { + // prepend the registration offset: + this.tx -= regX; this.ty -= regY; + } + if (skewX || skewY) { + // TODO: can this be combined into a single prepend operation? + skewX *= Matrix2D.DEG_TO_RAD; + skewY *= Matrix2D.DEG_TO_RAD; + this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); + this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); + } else { + this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); + } + return this; + }; + + /** + * Applies a clockwise rotation transformation to the matrix. + * @method rotate + * @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.rotate = function(angle) { + angle = angle*Matrix2D.DEG_TO_RAD; + var cos = Math.cos(angle); + var sin = Math.sin(angle); + + var a1 = this.a; + var b1 = this.b; + + this.a = a1*cos+this.c*sin; + this.b = b1*cos+this.d*sin; + this.c = -a1*sin+this.c*cos; + this.d = -b1*sin+this.d*cos; + return this; + }; + + /** + * Applies a skew transformation to the matrix. + * @method skew + * @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`. + * @param {Number} skewY The amount to skew vertically in degrees. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + */ + p.skew = function(skewX, skewY) { + skewX = skewX*Matrix2D.DEG_TO_RAD; + skewY = skewY*Matrix2D.DEG_TO_RAD; + this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0); + return this; + }; + + /** + * Applies a scale transformation to the matrix. + * @method scale + * @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it. + * @param {Number} y The amount to scale vertically. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.scale = function(x, y) { + this.a *= x; + this.b *= x; + this.c *= y; + this.d *= y; + //this.tx *= x; + //this.ty *= y; + return this; + }; + + /** + * Translates the matrix on the x and y axes. + * @method translate + * @param {Number} x + * @param {Number} y + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.translate = function(x, y) { + this.tx += this.a*x + this.c*y; + this.ty += this.b*x + this.d*y; + return this; + }; + + /** + * Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation). + * @method identity + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.identity = function() { + this.a = this.d = 1; + this.b = this.c = this.tx = this.ty = 0; + return this; + }; + + /** + * Inverts the matrix, causing it to perform the opposite transformation. + * @method invert + * @return {Matrix2D} This matrix. Useful for chaining method calls. + **/ + p.invert = function() { + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + return this; + }; + + /** + * Returns true if the matrix is an identity matrix. + * @method isIdentity + * @return {Boolean} + **/ + p.isIdentity = function() { + return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1; + }; + + /** + * Returns true if this matrix is equal to the specified matrix (all property values are equal). + * @method equals + * @param {Matrix2D} matrix The matrix to compare. + * @return {Boolean} + **/ + p.equals = function(matrix) { + return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d; + }; + + /** + * Transforms a point according to this matrix. + * @method transformPoint + * @param {Number} x The x component of the point to transform. + * @param {Number} y The y component of the point to transform. + * @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned. + * @return {Point} This matrix. Useful for chaining method calls. + **/ + p.transformPoint = function(x, y, pt) { + pt = pt||{}; + pt.x = x*this.a+y*this.c+this.tx; + pt.y = x*this.b+y*this.d+this.ty; + return pt; + }; + + /** + * Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values + * may not match the transform properties you used to generate the matrix, though they will produce the same visual + * results. + * @method decompose + * @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned. + * @return {Object} The target, or a new generic object with the transform properties applied. + */ + p.decompose = function(target) { + // TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative + if (target == null) { target = {}; } + target.x = this.tx; + target.y = this.ty; + target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b); + target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d); + + var skewX = Math.atan2(-this.c, this.d); + var skewY = Math.atan2(this.b, this.a); + + var delta = Math.abs(1-skewX/skewY); + if (delta < 0.00001) { // effectively identical, can use rotation: + target.rotation = skewY/Matrix2D.DEG_TO_RAD; + if (this.a < 0 && this.d >= 0) { + target.rotation += (target.rotation <= 0) ? 180 : -180; + } + target.skewX = target.skewY = 0; + } else { + target.skewX = skewX/Matrix2D.DEG_TO_RAD; + target.skewY = skewY/Matrix2D.DEG_TO_RAD; + } + return target; + }; + + /** + * Copies all properties from the specified matrix to this matrix. + * @method copy + * @param {Matrix2D} matrix The matrix to copy properties from. + * @return {Matrix2D} This matrix. Useful for chaining method calls. + */ + p.copy = function(matrix) { + return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); + }; + + /** + * Returns a clone of the Matrix2D instance. + * @method clone + * @return {Matrix2D} a clone of the Matrix2D instance. + **/ + p.clone = function() { + return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]"; + }; + + // this has to be populated after the class is defined: + Matrix2D.identity = new Matrix2D(); + + + createjs.Matrix2D = Matrix2D; +}()); + +//############################################################################## +// DisplayProps.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + /** + * Used for calculating and encapsulating display related properties. + * @class DisplayProps + * @param {Number} [visible=true] Visible value. + * @param {Number} [alpha=1] Alpha value. + * @param {Number} [shadow=null] A Shadow instance or null. + * @param {Number} [compositeOperation=null] A compositeOperation value or null. + * @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix. + * @constructor + **/ + function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) { + this.setValues(visible, alpha, shadow, compositeOperation, matrix); + + // public properties: + // assigned in the setValues method. + /** + * Property representing the alpha that will be applied to a display object. + * @property alpha + * @type Number + **/ + + /** + * Property representing the shadow that will be applied to a display object. + * @property shadow + * @type Shadow + **/ + + /** + * Property representing the compositeOperation that will be applied to a display object. + * You can find a list of valid composite operations at: + * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing + * @property compositeOperation + * @type String + **/ + + /** + * Property representing the value for visible that will be applied to a display object. + * @property visible + * @type Boolean + **/ + + /** + * The transformation matrix that will be applied to a display object. + * @property matrix + * @type Matrix2D + **/ + } + var p = DisplayProps.prototype; + +// initialization: + /** + * Reinitializes the instance with the specified values. + * @method setValues + * @param {Number} [visible=true] Visible value. + * @param {Number} [alpha=1] Alpha value. + * @param {Number} [shadow=null] A Shadow instance or null. + * @param {Number} [compositeOperation=null] A compositeOperation value or null. + * @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix. + * @return {DisplayProps} This instance. Useful for chaining method calls. + * @chainable + */ + p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) { + this.visible = visible == null ? true : !!visible; + this.alpha = alpha == null ? 1 : alpha; + this.shadow = shadow; + this.compositeOperation = compositeOperation; + this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D(); + return this; + }; + +// public methods: + /** + * Appends the specified display properties. This is generally used to apply a child's properties its parent's. + * @method append + * @param {Boolean} visible desired visible value + * @param {Number} alpha desired alpha value + * @param {Shadow} shadow desired shadow value + * @param {String} compositeOperation desired composite operation value + * @param {Matrix2D} [matrix] a Matrix2D instance + * @return {DisplayProps} This instance. Useful for chaining method calls. + * @chainable + */ + p.append = function(visible, alpha, shadow, compositeOperation, matrix) { + this.alpha *= alpha; + this.shadow = shadow || this.shadow; + this.compositeOperation = compositeOperation || this.compositeOperation; + this.visible = this.visible && visible; + matrix&&this.matrix.appendMatrix(matrix); + return this; + }; + + /** + * Prepends the specified display properties. This is generally used to apply a parent's properties to a child's. + * For example, to get the combined display properties that would be applied to a child, you could use: + * + * var o = myDisplayObject; + * var props = new createjs.DisplayProps(); + * do { + * // prepend each parent's props in turn: + * props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix()); + * } while (o = o.parent); + * + * @method prepend + * @param {Boolean} visible desired visible value + * @param {Number} alpha desired alpha value + * @param {Shadow} shadow desired shadow value + * @param {String} compositeOperation desired composite operation value + * @param {Matrix2D} [matrix] a Matrix2D instance + * @return {DisplayProps} This instance. Useful for chaining method calls. + * @chainable + */ + p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) { + this.alpha *= alpha; + this.shadow = this.shadow || shadow; + this.compositeOperation = this.compositeOperation || compositeOperation; + this.visible = this.visible && visible; + matrix&&this.matrix.prependMatrix(matrix); + return this; + }; + + /** + * Resets this instance and its matrix to default values. + * @method identity + * @return {DisplayProps} This instance. Useful for chaining method calls. + * @chainable + */ + p.identity = function() { + this.visible = true; + this.alpha = 1; + this.shadow = this.compositeOperation = null; + this.matrix.identity(); + return this; + }; + + /** + * Returns a clone of the DisplayProps instance. Clones the associated matrix. + * @method clone + * @return {DisplayProps} a clone of the DisplayProps instance. + **/ + p.clone = function() { + return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone()); + }; + +// private methods: + + createjs.DisplayProps = DisplayProps; +})(); + +//############################################################################## +// Point.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Represents a point on a 2 dimensional x / y coordinate system. + * + *

Example

+ * + * var point = new createjs.Point(0, 100); + * + * @class Point + * @param {Number} [x=0] X position. + * @param {Number} [y=0] Y position. + * @constructor + **/ + function Point(x, y) { + this.setValues(x, y); + + + // public properties: + // assigned in the setValues method. + /** + * X position. + * @property x + * @type Number + **/ + + /** + * Y position. + * @property y + * @type Number + **/ + } + var p = Point.prototype; + +// public methods: + /** + * Sets the specified values on this instance. + * @method setValues + * @param {Number} [x=0] X position. + * @param {Number} [y=0] Y position. + * @return {Point} This instance. Useful for chaining method calls. + * @chainable + */ + p.setValues = function(x, y) { + this.x = x||0; + this.y = y||0; + return this; + }; + + /** + * Copies all properties from the specified point to this point. + * @method copy + * @param {Point} point The point to copy properties from. + * @return {Point} This point. Useful for chaining method calls. + * @chainable + */ + p.copy = function(point) { + this.x = point.x; + this.y = point.y; + return this; + }; + + /** + * Returns a clone of the Point instance. + * @method clone + * @return {Point} a clone of the Point instance. + **/ + p.clone = function() { + return new Point(this.x, this.y); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Point (x="+this.x+" y="+this.y+")]"; + }; + + + createjs.Point = Point; +}()); + +//############################################################################## +// Rectangle.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Represents a rectangle as defined by the points (x, y) and (x+width, y+height). + * + *

Example

+ * + * var rect = new createjs.Rectangle(0, 0, 100, 100); + * + * @class Rectangle + * @param {Number} [x=0] X position. + * @param {Number} [y=0] Y position. + * @param {Number} [width=0] The width of the Rectangle. + * @param {Number} [height=0] The height of the Rectangle. + * @constructor + **/ + function Rectangle(x, y, width, height) { + this.setValues(x, y, width, height); + + + // public properties: + // assigned in the setValues method. + /** + * X position. + * @property x + * @type Number + **/ + + /** + * Y position. + * @property y + * @type Number + **/ + + /** + * Width. + * @property width + * @type Number + **/ + + /** + * Height. + * @property height + * @type Number + **/ + } + var p = Rectangle.prototype; + +// public methods: + /** + * Sets the specified values on this instance. + * @method setValues + * @param {Number} [x=0] X position. + * @param {Number} [y=0] Y position. + * @param {Number} [width=0] The width of the Rectangle. + * @param {Number} [height=0] The height of the Rectangle. + * @return {Rectangle} This instance. Useful for chaining method calls. + * @chainable + */ + p.setValues = function(x, y, width, height) { + // don't forget to update docs in the constructor if these change: + this.x = x||0; + this.y = y||0; + this.width = width||0; + this.height = height||0; + return this; + }; + + /** + * Extends the rectangle's bounds to include the described point or rectangle. + * @method extend + * @param {Number} x X position of the point or rectangle. + * @param {Number} y Y position of the point or rectangle. + * @param {Number} [width=0] The width of the rectangle. + * @param {Number} [height=0] The height of the rectangle. + * @return {Rectangle} This instance. Useful for chaining method calls. + * @chainable + */ + p.extend = function(x, y, width, height) { + width = width||0; + height = height||0; + if (x+width > this.x+this.width) { this.width = x+width-this.x; } + if (y+height > this.y+this.height) { this.height = y+height-this.y; } + if (x < this.x) { this.width += this.x-x; this.x = x; } + if (y < this.y) { this.height += this.y-y; this.y = y; } + return this; + }; + + /** + * Adds the specified padding to the rectangle's bounds. + * @method pad + * @param {Number} top + * @param {Number} left + * @param {Number} bottom + * @param {Number} right + * @return {Rectangle} This instance. Useful for chaining method calls. + * @chainable + */ + p.pad = function(top, left, bottom, right) { + this.x -= left; + this.y -= top; + this.width += left+right; + this.height += top+bottom; + return this; + }; + + /** + * Copies all properties from the specified rectangle to this rectangle. + * @method copy + * @param {Rectangle} rectangle The rectangle to copy properties from. + * @return {Rectangle} This rectangle. Useful for chaining method calls. + * @chainable + */ + p.copy = function(rectangle) { + return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height); + }; + + /** + * Returns true if this rectangle fully encloses the described point or rectangle. + * @method contains + * @param {Number} x X position of the point or rectangle. + * @param {Number} y Y position of the point or rectangle. + * @param {Number} [width=0] The width of the rectangle. + * @param {Number} [height=0] The height of the rectangle. + * @return {Boolean} True if the described point or rectangle is contained within this rectangle. + */ + p.contains = function(x, y, width, height) { + width = width||0; + height = height||0; + return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height); + }; + + /** + * Returns a new rectangle which contains this rectangle and the specified rectangle. + * @method union + * @param {Rectangle} rect The rectangle to calculate a union with. + * @return {Rectangle} A new rectangle describing the union. + */ + p.union = function(rect) { + return this.clone().extend(rect.x, rect.y, rect.width, rect.height); + }; + + /** + * Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle, + * or null if they do not intersect. + * @method intersection + * @param {Rectangle} rect The rectangle to calculate an intersection with. + * @return {Rectangle} A new rectangle describing the intersection or null. + */ + p.intersection = function(rect) { + var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height; + if (this.x > x1) { x1 = this.x; } + if (this.y > y1) { y1 = this.y; } + if (this.x + this.width < x2) { x2 = this.x + this.width; } + if (this.y + this.height < y2) { y2 = this.y + this.height; } + return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1); + }; + + /** + * Returns true if the specified rectangle intersects (has any overlap) with this rectangle. + * @method intersects + * @param {Rectangle} rect The rectangle to compare. + * @return {Boolean} True if the rectangles intersect. + */ + p.intersects = function(rect) { + return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height); + }; + + /** + * Returns true if the width or height are equal or less than 0. + * @method isEmpty + * @return {Boolean} True if the rectangle is empty. + */ + p.isEmpty = function() { + return this.width <= 0 || this.height <= 0; + }; + + /** + * Returns a clone of the Rectangle instance. + * @method clone + * @return {Rectangle} a clone of the Rectangle instance. + **/ + p.clone = function() { + return new Rectangle(this.x, this.y, this.width, this.height); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]"; + }; + + + createjs.Rectangle = Rectangle; +}()); + +//############################################################################## +// ButtonHelper.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or + * {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and + * automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, + * to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame. + * + * The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent + * garbage collection. + * + * Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. + * + *

Example

+ * + * var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit"); + * myInstance.addEventListener("click", handleClick); + * function handleClick(event) { + * // Click Happened. + * } + * + * @class ButtonHelper + * @param {Sprite|MovieClip} target The instance to manage. + * @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button. + * @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button. + * @param {String} [downLabel="down"] The label or animation to go to when the user presses the button. + * @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing + * states. + * @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined, + * then the button's visible states will be used instead. Note that the same instance as the "target" argument can be + * used for the hitState. + * @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is + * null, then the default state of the hitArea will be used. * + * @constructor + */ + function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) { + if (!target.addEventListener) { return; } + + + // public properties: + /** + * The target for this button helper. + * @property target + * @type MovieClip | Sprite + * @readonly + **/ + this.target = target; + + /** + * The label name or frame number to display when the user mouses out of the target. Defaults to "over". + * @property overLabel + * @type String | Number + **/ + this.overLabel = overLabel == null ? "over" : overLabel; + + /** + * The label name or frame number to display when the user mouses over the target. Defaults to "out". + * @property outLabel + * @type String | Number + **/ + this.outLabel = outLabel == null ? "out" : outLabel; + + /** + * The label name or frame number to display when the user presses on the target. Defaults to "down". + * @property downLabel + * @type String | Number + **/ + this.downLabel = downLabel == null ? "down" : downLabel; + + /** + * If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false. + * @property play + * @default false + * @type Boolean + **/ + this.play = play; + + + // private properties + /** + * @property _isPressed + * @type Boolean + * @protected + **/ + this._isPressed = false; + + /** + * @property _isOver + * @type Boolean + * @protected + **/ + this._isOver = false; + + /** + * @property _enabled + * @type Boolean + * @protected + **/ + this._enabled = false; + + // setup: + target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes. + this.enabled = true; + this.handleEvent({}); + if (hitArea) { + if (hitLabel) { + hitArea.actionsEnabled = false; + hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel); + } + target.hitArea = hitArea; + } + } + var p = ButtonHelper.prototype; + +// getter / setters: + /** + * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead. + * @method setEnabled + * @param {Boolean} value The enabled property to set the instance to. + * @[rptected + * @protected + **/ + p._setEnabled = function(value) { + if (value == this._enabled) { return; } + var o = this.target; + this._enabled = value; + if (value) { + o.cursor = "pointer"; + o.addEventListener("rollover", this); + o.addEventListener("rollout", this); + o.addEventListener("mousedown", this); + o.addEventListener("pressup", this); + if (o._reset) { o.__reset = o._reset; o._reset = this._reset;} + } else { + o.cursor = null; + o.removeEventListener("rollover", this); + o.removeEventListener("rollout", this); + o.removeEventListener("mousedown", this); + o.removeEventListener("pressup", this); + if (o.__reset) { o._reset = o.__reset; delete(o.__reset); } + } + }; + // ButtonHelper.setEnabled is @deprecated. Remove for 1.1+ + p.setEnabled = createjs.deprecate(p._setEnabled, "ButtonHelper.setEnabled"); + + /** + * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead. + * @method getEnabled + * @protected + * @return {Boolean} + **/ + p._getEnabled = function() { + return this._enabled; + }; + // ButtonHelper.getEnabled is @deprecated. Remove for 1.1+ + p.getEnabled = createjs.deprecate(p._getEnabled, "ButtonHelper.getEnabled"); + + /** + * Enables or disables the button functionality on the target. + * @property enabled + * @type {Boolean} + **/ + try { + Object.defineProperties(p, { + enabled: { get: p._getEnabled, set: p._setEnabled } + }); + } catch (e) {} // TODO: use Log + + +// public methods: + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[ButtonHelper]"; + }; + + +// private methods: + /** + * @method handleEvent + * @param {Object} evt The mouse event to handle. + * @protected + **/ + p.handleEvent = function(evt) { + var label, t = this.target, type = evt.type; + if (type == "mousedown") { + this._isPressed = true; + label = this.downLabel; + } else if (type == "pressup") { + this._isPressed = false; + label = this._isOver ? this.overLabel : this.outLabel; + } else if (type == "rollover") { + this._isOver = true; + label = this._isPressed ? this.downLabel : this.overLabel; + } else { // rollout and default + this._isOver = false; + label = this._isPressed ? this.overLabel : this.outLabel; + } + if (this.play) { + t.gotoAndPlay&&t.gotoAndPlay(label); + } else { + t.gotoAndStop&&t.gotoAndStop(label); + } + }; + + /** + * Injected into target. Preserves the paused state through a reset. + * @method _reset + * @protected + **/ + p._reset = function() { + // TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures. + var p = this.paused; + this.__reset(); + this.paused = p; + }; + + + createjs.ButtonHelper = ButtonHelper; +}()); + +//############################################################################## +// Shadow.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}} + * via its shadow property. + * + *

Example

+ * + * myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10); + * + * @class Shadow + * @constructor + * @param {String} color The color of the shadow. This can be any valid CSS color value. + * @param {Number} offsetX The x offset of the shadow in pixels. + * @param {Number} offsetY The y offset of the shadow in pixels. + * @param {Number} blur The size of the blurring effect. + **/ + function Shadow(color, offsetX, offsetY, blur) { + + + // public properties: + /** + * The color of the shadow. This can be any valid CSS color value. + * @property color + * @type String + * @default null + */ + this.color = color||"black"; + + /** The x offset of the shadow. + * @property offsetX + * @type Number + * @default 0 + */ + this.offsetX = offsetX||0; + + /** The y offset of the shadow. + * @property offsetY + * @type Number + * @default 0 + */ + this.offsetY = offsetY||0; + + /** The blur of the shadow. + * @property blur + * @type Number + * @default 0 + */ + this.blur = blur||0; + } + var p = Shadow.prototype; + +// static public properties: + /** + * An identity shadow object (all properties are set to 0). + * @property identity + * @type Shadow + * @static + * @final + * @readonly + **/ + Shadow.identity = new Shadow("transparent", 0, 0, 0); + + +// public methods: + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Shadow]"; + }; + + /** + * Returns a clone of this Shadow instance. + * @method clone + * @return {Shadow} A clone of the current Shadow instance. + **/ + p.clone = function() { + return new Shadow(this.color, this.offsetX, this.offsetY, this.blur); + }; + + + createjs.Shadow = Shadow; +}()); + +//############################################################################## +// SpriteSheet.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually + * animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100 + * images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high). + * + * The data passed to the SpriteSheet constructor defines: + *
    + *
  1. The source image or images to use.
  2. + *
  3. The positions of individual image frames.
  4. + *
  5. Sequences of frames that form named animations. Optional.
  6. + *
  7. The target playback framerate. Optional.
  8. + *
+ *

SpriteSheet Format

+ * SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties + * (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON. + * + *

images

+ * An array of source images. Images can be either an HTMlimage + * instance, or a uri to an image. The former is recommended to control preloading. + * + * images: [image1, "path/to/image2.png"], + * + *

frames

+ * Defines the individual frames. There are two supported formats for frame data: + * When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`, + * and `count` properties. + * + *
    + *
  • `width` & `height` are required and specify the dimensions of the frames
  • + *
  • `regX` & `regY` indicate the registration point or "origin" of the frames
  • + *
  • `spacing` indicate the spacing between frames
  • + *
  • `margin` specify the margin around the image(s)
  • + *
  • `count` allows you to specify the total number of frames in the spritesheet; if omitted, this will + * be calculated based on the dimensions of the source images and the frames. Frames will be assigned + * indexes based on their position in the source images (left to right, top to bottom).
  • + *
+ * + * frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0} + * + * If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array + * with 4 required and 3 optional entries, in the order: + * + *
    + *
  • The first four, `x`, `y`, `width`, and `height` are required and define the frame rectangle.
  • + *
  • The fifth, `imageIndex`, specifies the index of the source image (defaults to 0)
  • + *
  • The last two, `regX` and `regY` specify the registration point of the frame
  • + *
+ * + * frames: [ + * // x, y, width, height, imageIndex*, regX*, regY* + * [64, 0, 96, 64], + * [0, 0, 64, 64, 1, 32, 32] + * // etc. + * ] + * + *

animations

+ * Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an + * animation of the same name. Each animation must specify the frames to play, and may + * also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and + * the name of the `next` animation to sequence to after it completes. + * + * There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate: + *
    + *
  1. for a single frame animation, you can simply specify the frame index + * + * animations: { + * sit: 7 + * } + * + *
  2. + *
  3. + * for an animation of consecutive frames, you can use an array with two required, and two optional entries + * in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive. + * + * animations: { + * // start, end, next*, speed* + * run: [0, 8], + * jump: [9, 12, "run", 2] + * } + * + *
  4. + *
  5. + * for non-consecutive frames, you can use an object with a `frames` property defining an array of frame + * indexes to play in order. The object can also specify `next` and `speed` properties. + * + * animations: { + * walk: { + * frames: [1,2,3,3,2,1] + * }, + * shoot: { + * frames: [1,4,5,6], + * next: "walk", + * speed: 0.5 + * } + * } + * + *
  6. + *
+ * Note: the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency` + * property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in + * earlier versions, but is 4x normal speed in EaselJS 0.7.0+. + * + *

framerate

+ * Optional. Indicates the default framerate to play this spritesheet at in frames per second. See + * {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information. + * + * framerate: 20 + * + * Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}} + * event generated by the {{#crossLink "Ticker"}}{{/crossLink}}. + * + * createjs.Ticker.on("tick", handleTick); + * function handleTick(event) { + * stage.update(event); + * } + * + *

Example

+ * To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three + * animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and + * sequencing back to run. + * + * var data = { + * images: ["sprites.jpg"], + * frames: {width:50, height:50}, + * animations: { + * stand:0, + * run:[1,5], + * jump:[6,8,"run"] + * } + * }; + * var spriteSheet = new createjs.SpriteSheet(data); + * var animation = new createjs.Sprite(spriteSheet, "run"); + * + *

Generating SpriteSheet Images

+ * Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or + * coordinates manually, however there are a number of tools that facilitate this. + *
    + *
  • Exporting SpriteSheets or HTML5 content from Adobe Flash/Animate supports the EaselJS SpriteSheet format.
  • + *
  • The popular Texture Packer has + * EaselJS support. + *
  • SWF animations in Adobe Flash/Animate can be exported to SpriteSheets using Zoë
  • + *
+ * + *

Cross Origin Issues

+ * Warning: Images loaded cross-origin will throw cross-origin security errors when interacted with + * using: + *
    + *
  • a mouse
  • + *
  • methods such as {{#crossLink "Container/getObjectUnderPoint"}}{{/crossLink}}
  • + *
  • Filters (see {{#crossLink "Filter"}}{{/crossLink}})
  • + *
  • caching (see {{#crossLink "DisplayObject/cache"}}{{/crossLink}})
  • + *
+ * You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or + * setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems. + * + * var image = new Image(); + * img.crossOrigin="Anonymous"; + * img.src = "http://server-with-CORS-support.com/path/to/image.jpg"; + * + * If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must + * support cross-origin requests, or this will not work. For more information, check out + * CORS overview on MDN. + * + * @class SpriteSheet + * @constructor + * @param {Object} data An object describing the SpriteSheet data. + * @extends EventDispatcher + **/ + function SpriteSheet(data) { + this.EventDispatcher_constructor(); + + + // public properties: + /** + * Indicates whether all images are finished loading. + * @property complete + * @type Boolean + * @readonly + **/ + this.complete = true; + + /** + * Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class + * {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information. + * @property framerate + * @type Number + **/ + this.framerate = 0; + + + // private properties: + /** + * @property _animations + * @protected + * @type Array + **/ + this._animations = null; + + /** + * @property _frames + * @protected + * @type Array + **/ + this._frames = null; + + /** + * @property _images + * @protected + * @type Array + **/ + this._images = null; + + /** + * @property _data + * @protected + * @type Object + **/ + this._data = null; + + /** + * @property _loadCount + * @protected + * @type Number + **/ + this._loadCount = 0; + + // only used for simple frame defs: + /** + * @property _frameHeight + * @protected + * @type Number + **/ + this._frameHeight = 0; + + /** + * @property _frameWidth + * @protected + * @type Number + **/ + this._frameWidth = 0; + + /** + * @property _numFrames + * @protected + * @type Number + **/ + this._numFrames = 0; + + /** + * @property _regX + * @protected + * @type Number + **/ + this._regX = 0; + + /** + * @property _regY + * @protected + * @type Number + **/ + this._regY = 0; + + /** + * @property _spacing + * @protected + * @type Number + **/ + this._spacing = 0; + + /** + * @property _margin + * @protected + * @type Number + **/ + this._margin = 0; + + // setup: + this._parseData(data); + } + var p = createjs.extend(SpriteSheet, createjs.EventDispatcher); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. + + +// events: + /** + * Dispatched when all images are loaded. Note that this only fires if the images + * were not fully loaded when the sprite sheet was initialized. You should check the complete property + * to prior to adding a listener. Ex. + * + * var sheet = new createjs.SpriteSheet(data); + * if (!sheet.complete) { + * // not preloaded, listen for the complete event: + * sheet.addEventListener("complete", handler); + * } + * + * @event complete + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.6.0 + */ + + /** + * Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} + * when doing on-demand rendering. + * @event getframe + * @param {Number} index The frame index. + * @param {Object} frame The frame object that getFrame will return. + */ + + /** + * Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that + * encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}} + * event once all images are finished processing, even if an error is encountered. + * @event error + * @param {String} src The source of the image that failed to load. + * @since 0.8.2 + */ + + +// getter / setters: + /** + * Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead. + * @method _getAnimations + * @protected + * @return {Array} + **/ + p._getAnimations = function() { + return this._animations.slice(); + }; + // SpriteSheet.getAnimations is @deprecated. Remove for 1.1+ + p.getAnimations = createjs.deprecate(p._getAnimations, "SpriteSheet.getAnimations"); + + /** + * Returns an array of all available animation names available on this sprite sheet as strings. + * @property animations + * @type {Array} + * @readonly + **/ + try { + Object.defineProperties(p, { + animations: { get: p._getAnimations } + }); + } catch (e) {} + + +// public methods: + /** + * Returns the total number of frames in the specified animation, or in the whole sprite + * sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and + * the images have not been fully loaded. + * @method getNumFrames + * @param {String} animation The name of the animation to get a frame count for. + * @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted. + */ + p.getNumFrames = function(animation) { + if (animation == null) { + return this._frames ? this._frames.length : this._numFrames || 0; + } else { + var data = this._data[animation]; + if (data == null) { return 0; } + else { return data.frames.length; } + } + }; + + /** + * Returns an object defining the specified animation. The returned object contains:
    + *
  • frames: an array of the frame ids in the animation
  • + *
  • speed: the playback speed for this animation
  • + *
  • name: the name of the animation
  • + *
  • next: the default animation to play next. If the animation loops, the name and next property will be the + * same.
  • + *
+ * @method getAnimation + * @param {String} name The name of the animation to get. + * @return {Object} a generic object with frames, speed, name, and next properties. + **/ + p.getAnimation = function(name) { + return this._data[name]; + }; + + /** + * Returns an object specifying the image and source rect of the specified frame. The returned object has:
    + *
  • an image property holding a reference to the image object in which the frame is found
  • + *
  • a rect property containing a Rectangle instance which defines the boundaries for the frame within that + * image.
  • + *
  • A regX and regY property corresponding to the regX/Y values for the frame. + *
+ * @method getFrame + * @param {Number} frameIndex The index of the frame. + * @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist. + **/ + p.getFrame = function(frameIndex) { + var frame; + if (this._frames && (frame=this._frames[frameIndex])) { return frame; } + return null; + }; + + /** + * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative + * to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return: + * + * [x=-50, y=-40, width=90, height=70] + * + * @method getFrameBounds + * @param {Number} frameIndex The index of the frame. + * @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created. + * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded. + **/ + p.getFrameBounds = function(frameIndex, rectangle) { + var frame = this.getFrame(frameIndex); + return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null; + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[SpriteSheet]"; + }; + + /** + * SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it. + * @method clone + **/ + p.clone = function() { + throw("SpriteSheet cannot be cloned.") + }; + +// private methods: + /** + * @method _parseData + * @param {Object} data An object describing the SpriteSheet data. + * @protected + **/ + p._parseData = function(data) { + var i,l,o,a; + if (data == null) { return; } + + this.framerate = data.framerate||0; + + // parse images: + if (data.images && (l=data.images.length) > 0) { + a = this._images = []; + for (i=0; i= maxFrames) { break imgLoop; } + frameCount++; + this._frames.push({ + image: img, + rect: new createjs.Rectangle(x, y, frameWidth, frameHeight), + regX: this._regX, + regY: this._regY + }); + x += frameWidth+spacing; + } + y += frameHeight+spacing; + } + } + this._numFrames = frameCount; + }; + + + createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher"); +}()); + +//############################################################################## +// Graphics.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a + * specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}} + * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the + * context of an EaselJS display list. + * + * There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or + * instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}. + * The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes. + * + * var g = new createjs.Graphics(); + * g.setStrokeStyle(1); + * g.beginStroke("#000000"); + * g.beginFill("red"); + * g.drawCircle(0,0,30); + * + * All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example, + * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill: + * + * myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50); + * + * Each graphics API call generates a command object (see below). The last command to be created can be accessed via + * {{#crossLink "Graphics/command:property"}}{{/crossLink}}: + * + * var fillCommand = myGraphics.beginFill("red").command; + * // ... later, update the fill style/color: + * fillCommand.style = "blue"; + * // or change it to a bitmap fill: + * fillCommand.bitmap(myImage); + * + * For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you + * need to manage path creation manually, and ensure that fill/stroke is applied to a defined path: + * + * // start a new path. Graphics.beginCmd is a reusable BeginPath instance: + * myGraphics.append(createjs.Graphics.beginCmd); + * // we need to define the path before applying the fill: + * var circle = new createjs.Graphics.Circle(0,0,30); + * myGraphics.append(circle); + * // fill the path we just defined: + * var fill = new createjs.Graphics.Fill("red"); + * myGraphics.append(fill); + * + * These approaches can be used together, for example to insert a custom command: + * + * myGraphics.beginFill("red"); + * var customCommand = new CustomSpiralCommand(etc); + * myGraphics.append(customCommand); + * myGraphics.beginFill("blue"); + * myGraphics.drawCircle(0, 0, 30); + * + * See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands. + * + *

Tiny API

+ * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the + * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS + * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected + * descriptions in the docs. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TinyMethodTinyMethod
mt{{#crossLink "Graphics/moveTo"}}{{/crossLink}} lt {{#crossLink "Graphics/lineTo"}}{{/crossLink}}
a/at{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} bt{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}}
qt{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)r{{#crossLink "Graphics/rect"}}{{/crossLink}}
cp{{#crossLink "Graphics/closePath"}}{{/crossLink}} c{{#crossLink "Graphics/clear"}}{{/crossLink}}
f{{#crossLink "Graphics/beginFill"}}{{/crossLink}} lf{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}}
rf{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} bf{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}}
ef{{#crossLink "Graphics/endFill"}}{{/crossLink}} ss / sd{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}}
s{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} ls{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}}
rs{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} bs{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}}
es{{#crossLink "Graphics/endStroke"}}{{/crossLink}} dr{{#crossLink "Graphics/drawRect"}}{{/crossLink}}
rr{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} rc{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}}
dc{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} de{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}}
dp{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} p{{#crossLink "Graphics/decodePath"}}{{/crossLink}}
+ * + * Here is the above example, using the tiny API instead. + * + * myGraphics.s("red").f("blue").r(20, 20, 100, 50); + * + * @class Graphics + * @constructor + **/ + function Graphics() { + + + // public properties + /** + * Holds a reference to the last command that was created or appended. For example, you could retain a reference + * to a Fill command in order to dynamically update the color later by using: + * + * var myFill = myGraphics.beginFill("red").command; + * // update color later: + * myFill.style = "yellow"; + * + * @property command + * @type Object + **/ + this.command = null; + + + // private properties + /** + * @property _stroke + * @protected + * @type {Stroke} + **/ + this._stroke = null; + + /** + * @property _strokeStyle + * @protected + * @type {StrokeStyle} + **/ + this._strokeStyle = null; + + /** + * @property _oldStrokeStyle + * @protected + * @type {StrokeStyle} + **/ + this._oldStrokeStyle = null; + + /** + * @property _strokeDash + * @protected + * @type {StrokeDash} + **/ + this._strokeDash = null; + + /** + * @property _oldStrokeDash + * @protected + * @type {StrokeDash} + **/ + this._oldStrokeDash = null; + + /** + * @property _strokeIgnoreScale + * @protected + * @type Boolean + **/ + this._strokeIgnoreScale = false; + + /** + * @property _fill + * @protected + * @type {Fill} + **/ + this._fill = null; + + /** + * @property _instructions + * @protected + * @type {Array} + **/ + this._instructions = []; + + /** + * Indicates the last instruction index that was committed. + * @property _commitIndex + * @protected + * @type {Number} + **/ + this._commitIndex = 0; + + /** + * Uncommitted instructions. + * @property _activeInstructions + * @protected + * @type {Array} + **/ + this._activeInstructions = []; + + /** + * This indicates that there have been changes to the activeInstruction list since the last updateInstructions call. + * @property _dirty + * @protected + * @type {Boolean} + * @default false + **/ + this._dirty = false; + + /** + * Index to draw from if a store operation has happened. + * @property _storeIndex + * @protected + * @type {Number} + * @default 0 + **/ + this._storeIndex = 0; + + // setup: + this.clear(); + } + var p = Graphics.prototype; + var G = Graphics; // shortcut + +// static public methods: + /** + * Returns a CSS compatible color string based on the specified RGB numeric color values in the format + * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example, + * + * createjs.Graphics.getRGB(50, 100, 150, 0.5); + * // Returns "rgba(50,100,150,0.5)" + * + * It also supports passing a single hex color value as the first param, and an optional alpha value as the second + * param. For example, + * + * createjs.Graphics.getRGB(0xFF00FF, 0.2); + * // Returns "rgba(255,0,255,0.2)" + * + * @method getRGB + * @static + * @param {Number} r The red component for the color, between 0 and 0xFF (255). + * @param {Number} g The green component for the color, between 0 and 0xFF (255). + * @param {Number} b The blue component for the color, between 0 and 0xFF (255). + * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. + * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format + * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". + **/ + Graphics.getRGB = function(r, g, b, alpha) { + if (r != null && b == null) { + alpha = g; + b = r&0xFF; + g = r>>8&0xFF; + r = r>>16&0xFF; + } + if (alpha == null) { + return "rgb("+r+","+g+","+b+")"; + } else { + return "rgba("+r+","+g+","+b+","+alpha+")"; + } + }; + + /** + * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)", + * or if alpha is null then in the format "hsl(360,100,100)". + * + * createjs.Graphics.getHSL(150, 100, 70); + * // Returns "hsl(150,100,70)" + * + * @method getHSL + * @static + * @param {Number} hue The hue component for the color, between 0 and 360. + * @param {Number} saturation The saturation component for the color, between 0 and 100. + * @param {Number} lightness The lightness component for the color, between 0 and 100. + * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque. + * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format + * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)". + **/ + Graphics.getHSL = function(hue, saturation, lightness, alpha) { + if (alpha == null) { + return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)"; + } else { + return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")"; + } + }; + + +// static properties: + /** + * A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid + * unnecessary instantiation. + * @property beginCmd + * @type {Graphics.BeginPath} + * @static + **/ + // defined at the bottom of this file. + + /** + * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}. + * @property BASE_64 + * @static + * @final + * @readonly + * @type {Object} + **/ + Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63}; + + /** + * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to + * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to + * "butt", 1 to "round", and 2 to "square". + * For example, to set the line caps to "square": + * + * myGraphics.ss(16, 2); + * + * @property STROKE_CAPS_MAP + * @static + * @final + * @readonly + * @type {Array} + **/ + Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"]; + + /** + * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to + * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to + * "miter", 1 to "round", and 2 to "bevel". + * For example, to set the line joints to "bevel": + * + * myGraphics.ss(16, 0, 2); + * + * @property STROKE_JOINTS_MAP + * @static + * @final + * @readonly + * @type {Array} + **/ + Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"]; + + /** + * @property _ctx + * @static + * @protected + * @type {CanvasRenderingContext2D} + **/ + var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); + if (canvas.getContext) { + Graphics._ctx = canvas.getContext("2d"); + canvas.width = canvas.height = 1; + } + + +// getter / setters: + /** + * Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead. + * @method _getInstructions + * @protected + * @return {Array} The instructions array, useful for chaining + **/ + p._getInstructions = function() { + this._updateInstructions(); + return this._instructions; + }; + // Graphics.getInstructions is @deprecated. Remove for 1.1+ + p.getInstructions = createjs.deprecate(p._getInstructions, "Graphics.getInstructions"); + + /** + * Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect) + * Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour. + * + * This property is mainly intended for introspection of the instructions (ex. for graphics export). + * @property instructions + * @type {Array} + * @readonly + **/ + try { + Object.defineProperties(p, { + instructions: { get: p._getInstructions } + }); + } catch (e) {} + + +// public methods: + /** + * Returns true if this Graphics instance has no drawing commands. + * @method isEmpty + * @return {Boolean} Returns true if this Graphics instance has no drawing commands. + **/ + p.isEmpty = function() { + return !(this._instructions.length || this._activeInstructions.length); + }; + + /** + * Draws the display object 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 {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data. + **/ + p.draw = function(ctx, data) { + this._updateInstructions(); + var instr = this._instructions; + for (var i=this._storeIndex, l=instr.length; iDisplayObject.mask to draw the clipping path, for example. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method drawAsPath + * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into. + **/ + p.drawAsPath = function(ctx) { + this._updateInstructions(); + var instr, instrs = this._instructions; + for (var i=this._storeIndex, l=instrs.length; i + * whatwg spec. + * @method lineTo + * @param {Number} x The x coordinate the drawing point should draw to. + * @param {Number} y The y coordinate the drawing point should draw to. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.lineTo = function(x, y) { + return this.append(new G.LineTo(x,y)); + }; + + /** + * Draws an arc with the specified control points and radius. For detailed information, read the + * + * whatwg spec. A tiny API method "at" also exists. + * @method arcTo + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} x2 + * @param {Number} y2 + * @param {Number} radius + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.arcTo = function(x1, y1, x2, y2, radius) { + return this.append(new G.ArcTo(x1, y1, x2, y2, radius)); + }; + + /** + * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For + * example, to draw a full circle with a radius of 20 centered at (100, 100): + * + * arc(100, 100, 20, 0, Math.PI*2); + * + * For detailed information, read the + * whatwg spec. + * A tiny API method "a" also exists. + * @method arc + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} startAngle Measured in radians. + * @param {Number} endAngle Measured in radians. + * @param {Boolean} anticlockwise + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) { + return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise)); + }; + + /** + * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed + * information, read the + * whatwg spec. A tiny API method "qt" also exists. + * @method quadraticCurveTo + * @param {Number} cpx + * @param {Number} cpy + * @param {Number} x + * @param {Number} y + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.quadraticCurveTo = function(cpx, cpy, x, y) { + return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y)); + }; + + /** + * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x, + * cp2y). For detailed information, read the + * + * whatwg spec. A tiny API method "bt" also exists. + * @method bezierCurveTo + * @param {Number} cp1x + * @param {Number} cp1y + * @param {Number} cp2x + * @param {Number} cp2y + * @param {Number} x + * @param {Number} y + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { + return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)); + }; + + /** + * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke. + * For detailed information, read the + * + * whatwg spec. A tiny API method "r" also exists. + * @method rect + * @param {Number} x + * @param {Number} y + * @param {Number} w Width of the rectangle + * @param {Number} h Height of the rectangle + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.rect = function(x, y, w, h) { + return this.append(new G.Rect(x, y, w, h)); + }; + + /** + * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified + * since the fill or stroke was last set. A tiny API method "cp" also exists. + * @method closePath + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.closePath = function() { + return this._activeInstructions.length ? this.append(new G.ClosePath()) : this; + }; + + +// public methods that roughly map to Adobe Flash/Animate graphics APIs: + /** + * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need + * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists. + * @method clear + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.clear = function() { + this._instructions.length = this._activeInstructions.length = this._commitIndex = 0; + this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null; + this._dirty = this._strokeIgnoreScale = false; + return this; + }; + + /** + * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists. + * @method beginFill + * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to + * null will result in no fill. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginFill = function(color) { + return this._setFill(color ? new G.Fill(color) : null); + }; + + /** + * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For + * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a + * square to display it: + * + * myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); + * + * A tiny API method "lf" also exists. + * @method beginLinearGradientFill + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient + * drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw + * the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. + * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) { + return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1)); + }; + + /** + * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to + * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it: + * + * myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50); + * + * A tiny API method "rf" also exists. + * @method beginRadialGradientFill + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 Center position of the inner circle that defines the gradient. + * @param {Number} y0 Center position of the inner circle that defines the gradient. + * @param {Number} r0 Radius of the inner circle that defines the gradient. + * @param {Number} x1 Center position of the outer circle that defines the gradient. + * @param {Number} y1 Center position of the outer circle that defines the gradient. + * @param {Number} r1 Radius of the outer circle that defines the gradient. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) { + return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1)); + }; + + /** + * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also + * exists. + * @method beginBitmapFill + * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use + * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty. + * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat", + * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or + * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat". + * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation + * will be applied relative to the parent transform. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginBitmapFill = function(image, repetition, matrix) { + return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition)); + }; + + /** + * Ends the current sub-path, and begins a new one with no fill. Functionally identical to beginFill(null). + * A tiny API method "ef" also exists. + * @method endFill + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.endFill = function() { + return this.beginFill(); + }; + + /** + * Sets the stroke style. Like all drawing methods, this can be chained, so you can define + * the stroke style and color in a single line of code like so: + * + * myGraphics.setStrokeStyle(8,"round").beginStroke("#F00"); + * + * A tiny API method "ss" also exists. + * @method setStrokeStyle + * @param {Number} thickness The width of the stroke. + * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt, + * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with + * the tiny API. + * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet. + * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) + * for use with the tiny API. + * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which + * controls at what point a mitered joint will be clipped. + * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless + * of active transformations. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) { + this._updateInstructions(true); + this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale); + + // ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery: + if (this._stroke) { this._stroke.ignoreScale = ignoreScale; } + this._strokeIgnoreScale = ignoreScale; + return this; + }; + + /** + * Sets or clears the stroke dash pattern. + * + * myGraphics.setStrokeDash([20, 10], 0); + * + * A tiny API method `sd` also exists. + * @method setStrokeDash + * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap. + * For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them. + * Passing null or an empty array will clear the existing stroke dash. + * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.setStrokeDash = function(segments, offset) { + this._updateInstructions(true); + this._strokeDash = this.command = new G.StrokeDash(segments, offset); + return this; + }; + + /** + * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists. + * @method beginStroke + * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to + * null will result in no stroke. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginStroke = function(color) { + return this._setStroke(color ? new G.Stroke(color) : null); + }; + + /** + * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For + * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a + * square to display it: + * + * myGraphics.setStrokeStyle(10). + * beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120); + * + * A tiny API method "ls" also exists. + * @method beginLinearGradientStroke + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. + * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) { + return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1)); + }; + + /** + * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to + * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it: + * + * myGraphics.setStrokeStyle(10) + * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50) + * .drawRect(50, 90, 150, 110); + * + * A tiny API method "rs" also exists. + * @method beginRadialGradientStroke + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color + * to 100%. + * @param {Number} x0 Center position of the inner circle that defines the gradient. + * @param {Number} y0 Center position of the inner circle that defines the gradient. + * @param {Number} r0 Radius of the inner circle that defines the gradient. + * @param {Number} x1 Center position of the outer circle that defines the gradient. + * @param {Number} y1 Center position of the outer circle that defines the gradient. + * @param {Number} r1 Radius of the outer circle that defines the gradient. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) { + return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1)); + }; + + /** + * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills, + * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs" + * also exists. + * @method beginBitmapStroke + * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use + * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty. + * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of + * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.beginBitmapStroke = function(image, repetition) { + // NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width. + return this._setStroke(new G.Stroke().bitmap(image, repetition)); + }; + + /** + * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to beginStroke(null). + * A tiny API method "es" also exists. + * @method endStroke + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.endStroke = function() { + return this.beginStroke(); + }; + + /** + * Maps the familiar ActionScript curveTo() method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} + * method. + * @method curveTo + * @param {Number} cpx + * @param {Number} cpy + * @param {Number} x + * @param {Number} y + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.curveTo = p.quadraticCurveTo; + + /** + * + * Maps the familiar ActionScript drawRect() method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}} + * method. + * @method drawRect + * @param {Number} x + * @param {Number} y + * @param {Number} w Width of the rectangle + * @param {Number} h Height of the rectangle + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawRect = p.rect; + + /** + * Draws a rounded rectangle with all corners with the specified radius. + * @method drawRoundRect + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} h + * @param {Number} radius Corner radius. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawRoundRect = function(x, y, w, h, radius) { + return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius); + }; + + /** + * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API + * method "rc" also exists. + * @method drawRoundRectComplex + * @param {Number} x The horizontal coordinate to draw the round rect. + * @param {Number} y The vertical coordinate to draw the round rect. + * @param {Number} w The width of the round rect. + * @param {Number} h The height of the round rect. + * @param {Number} radiusTL Top left corner radius. + * @param {Number} radiusTR Top right corner radius. + * @param {Number} radiusBR Bottom right corner radius. + * @param {Number} radiusBL Bottom left corner radius. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) { + return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL)); + }; + + /** + * Draws a circle with the specified radius at (x, y). + * + * var g = new createjs.Graphics(); + * g.setStrokeStyle(1); + * g.beginStroke(createjs.Graphics.getRGB(0,0,0)); + * g.beginFill(createjs.Graphics.getRGB(255,0,0)); + * g.drawCircle(0,0,3); + * + * var s = new createjs.Shape(g); + * s.x = 100; + * s.y = 100; + * + * stage.addChild(s); + * stage.update(); + * + * A tiny API method "dc" also exists. + * @method drawCircle + * @param {Number} x x coordinate center point of circle. + * @param {Number} y y coordinate center point of circle. + * @param {Number} radius Radius of circle. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawCircle = function(x, y, radius) { + return this.append(new G.Circle(x, y, radius)); + }; + + /** + * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}, + * except the width and height can be different. A tiny API method "de" also exists. + * @method drawEllipse + * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} + * which draws from center. + * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} + * which draws from the center. + * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this + * number. + * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawEllipse = function(x, y, w, h) { + return this.append(new G.Ellipse(x, y, w, h)); + }; + + /** + * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of + * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a + * radius of 50: + * + * myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90); + * // Note: -90 makes the first point vertical + * + * A tiny API method "dp" also exists. + * + * @method drawPolyStar + * @param {Number} x Position of the center of the shape. + * @param {Number} y Position of the center of the shape. + * @param {Number} radius The outer radius of the shape. + * @param {Number} sides The number of points on the star or sides on the polygon. + * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular + * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy. + * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point + * directly to the right of the center. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) { + return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle)); + }; + + /** + * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method + * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into + * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw. + * + * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert + * built-in or custom graphics commands. For example: + * + * // attach data to our shape, so we can access it during the draw: + * myShape.color = "red"; + * + * // append a Circle command object: + * myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30)); + * + * // append a custom command object with an exec method that sets the fill style + * // based on the shape's data, and then fills the circle. + * myShape.graphics.append({exec:function(ctx, shape) { + * ctx.fillStyle = shape.color; + * ctx.fill(); + * }}); + * + * @method append + * @param {Object} command A graphics command object exposing an "exec" method. + * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.append = function(command, clean) { + this._activeInstructions.push(command); + this.command = command; + if (!clean) { this._dirty = true; } + return this; + }; + + /** + * Decodes a compact encoded path string into a series of draw instructions. + * This format is not intended to be human readable, and is meant for use by authoring tools. + * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw + * commands. + * + * Each command is comprised of a single "header" character followed by a variable number of alternating x and y + * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the + * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4 + * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the + * latter. Bits 5 and 6 are currently unused. + * + * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo) + * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the + * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed + * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the + * case of move operations which are absolute, this value is a delta from the previous x or y position (as + * appropriate). + * + * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0. + *
A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per + * parameter. + *
n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits + * indicate 1500 tenths of a pixel. + *
AA - 000000000000. Absolute y position of 0. + *
I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter. + *
Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to + * provide an absolute position of +150.0px. + *
AAA - 000000000000000000. A y delta value of 0. + * + * A tiny API method "p" also exists. + * @method decodePath + * @param {String} str The path string to decode. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.decodePath = function(str) { + var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath]; + var paramCount = [2, 2, 4, 6, 0]; + var i=0, l=str.length; + var params = []; + var x=0, y=0; + var base64 = Graphics.BASE_64; + + while (i>3; // highest order bits 1-3 code for operation. + var f = instructions[fi]; + // check that we have a valid instruction & that the unused bits are empty: + if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); } + var pl = paramCount[fi]; + if (!fi) { x=y=0; } // move operations reset the position. + params.length = 0; + i++; + var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation. + for (var p=0; p>5) ? -1 : 1; + num = ((num&31)<<6)|(base64[str.charAt(i+1)]); + if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); } + num = sign*num/10; + if (p%2) { x = (num += x); } + else { y = (num += y); } + params[p] = num; + i += charCount; + } + f.apply(this,params); + } + return this; + }; + + /** + * Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to + * the existing store. This also affects `drawAsPath()`. + * + * This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so + * that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all + * of the vector instructions for later use (ex. scaling, modifying, or exporting). + * + * Note that calling store() will force the active path (if any) to be ended in a manner similar to changing + * the fill or stroke. + * + * For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of + * segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}, + * and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG. + * + * // set up cache: + * myShape.cache(0,0,500,500,scale); + * + * // when the user drags, draw a new line: + * myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY); + * // then draw it into the existing cache: + * myShape.updateCache("source-over"); + * // store the new line, so it isn't redrawn next time: + * myShape.store(); + * + * // then, when the window resizes, we can re-render at a different scale: + * // first, unstore all our lines: + * myShape.unstore(); + * // then cache using the new scale: + * myShape.cache(0,0,500,500,newScale); + * // finally, store the existing commands again: + * myShape.store(); + * + * @method store + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.store = function() { + this._updateInstructions(true); + this._storeIndex = this._instructions.length; + return this; + }; + + /** + * Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}} + * so that they will be executed in subsequent draw calls. + * + * @method unstore + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.unstore = function() { + this._storeIndex = 0; + return this; + }; + + /** + * Returns a clone of this Graphics instance. Note that the individual command objects are not cloned. + * @method clone + * @return {Graphics} A clone of the current Graphics instance. + **/ + p.clone = function() { + var o = new Graphics(); + o.command = this.command; + o._stroke = this._stroke; + o._strokeStyle = this._strokeStyle; + o._strokeDash = this._strokeDash; + o._strokeIgnoreScale = this._strokeIgnoreScale; + o._fill = this._fill; + o._instructions = this._instructions.slice(); + o._commitIndex = this._commitIndex; + o._activeInstructions = this._activeInstructions.slice(); + o._dirty = this._dirty; + o._storeIndex = this._storeIndex; + return o; + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Graphics]"; + }; + + +// tiny API: + /** + * Shortcut to moveTo. + * @method mt + * @param {Number} x The x coordinate the drawing point should move to. + * @param {Number} y The y coordinate the drawing point should move to. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls). + * @chainable + * @protected + **/ + p.mt = p.moveTo; + + /** + * Shortcut to lineTo. + * @method lt + * @param {Number} x The x coordinate the drawing point should draw to. + * @param {Number} y The y coordinate the drawing point should draw to. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.lt = p.lineTo; + + /** + * Shortcut to arcTo. + * @method at + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} x2 + * @param {Number} y2 + * @param {Number} radius + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.at = p.arcTo; + + /** + * Shortcut to bezierCurveTo. + * @method bt + * @param {Number} cp1x + * @param {Number} cp1y + * @param {Number} cp2x + * @param {Number} cp2y + * @param {Number} x + * @param {Number} y + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.bt = p.bezierCurveTo; + + /** + * Shortcut to quadraticCurveTo / curveTo. + * @method qt + * @param {Number} cpx + * @param {Number} cpy + * @param {Number} x + * @param {Number} y + * @protected + * @chainable + **/ + p.qt = p.quadraticCurveTo; + + /** + * Shortcut to arc. + * @method a + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} startAngle Measured in radians. + * @param {Number} endAngle Measured in radians. + * @param {Boolean} anticlockwise + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @protected + * @chainable + **/ + p.a = p.arc; + + /** + * Shortcut to rect. + * @method r + * @param {Number} x + * @param {Number} y + * @param {Number} w Width of the rectangle + * @param {Number} h Height of the rectangle + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.r = p.rect; + + /** + * Shortcut to closePath. + * @method cp + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.cp = p.closePath; + + /** + * Shortcut to clear. + * @method c + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.c = p.clear; + + /** + * Shortcut to beginFill. + * @method f + * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to + * null will result in no fill. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.f = p.beginFill; + + /** + * Shortcut to beginLinearGradientFill. + * @method lf + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient + * drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw + * the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. + * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.lf = p.beginLinearGradientFill; + + /** + * Shortcut to beginRadialGradientFill. + * @method rf + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 Center position of the inner circle that defines the gradient. + * @param {Number} y0 Center position of the inner circle that defines the gradient. + * @param {Number} r0 Radius of the inner circle that defines the gradient. + * @param {Number} x1 Center position of the outer circle that defines the gradient. + * @param {Number} y1 Center position of the outer circle that defines the gradient. + * @param {Number} r1 Radius of the outer circle that defines the gradient. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.rf = p.beginRadialGradientFill; + + /** + * Shortcut to beginBitmapFill. + * @method bf + * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use + * as the pattern. + * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat", + * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or + * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat". + * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation + * will be applied relative to the parent transform. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.bf = p.beginBitmapFill; + + /** + * Shortcut to endFill. + * @method ef + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.ef = p.endFill; + + /** + * Shortcut to setStrokeStyle. + * @method ss + * @param {Number} thickness The width of the stroke. + * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt, + * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with + * the tiny API. + * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet. + * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) + * for use with the tiny API. + * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which + * controls at what point a mitered joint will be clipped. + * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless + * of active transformations. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.ss = p.setStrokeStyle; + + /** + * Shortcut to setStrokeDash. + * @method sd + * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap. + * For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them. + * Passing null or an empty array will clear any existing dash. + * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.sd = p.setStrokeDash; + + /** + * Shortcut to beginStroke. + * @method s + * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to + * null will result in no stroke. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.s = p.beginStroke; + + /** + * Shortcut to beginLinearGradientStroke. + * @method ls + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%. + * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size. + * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size. + * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.ls = p.beginLinearGradientStroke; + + /** + * Shortcut to beginRadialGradientStroke. + * @method rs + * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define + * a gradient drawing from red to blue. + * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, + * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color + * to 100%. + * @param {Number} x0 Center position of the inner circle that defines the gradient. + * @param {Number} y0 Center position of the inner circle that defines the gradient. + * @param {Number} r0 Radius of the inner circle that defines the gradient. + * @param {Number} x1 Center position of the outer circle that defines the gradient. + * @param {Number} y1 Center position of the outer circle that defines the gradient. + * @param {Number} r1 Radius of the outer circle that defines the gradient. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.rs = p.beginRadialGradientStroke; + + /** + * Shortcut to beginBitmapStroke. + * @method bs + * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use + * as the pattern. + * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of + * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.bs = p.beginBitmapStroke; + + /** + * Shortcut to endStroke. + * @method es + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.es = p.endStroke; + + /** + * Shortcut to drawRect. + * @method dr + * @param {Number} x + * @param {Number} y + * @param {Number} w Width of the rectangle + * @param {Number} h Height of the rectangle + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.dr = p.drawRect; + + /** + * Shortcut to drawRoundRect. + * @method rr + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} h + * @param {Number} radius Corner radius. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.rr = p.drawRoundRect; + + /** + * Shortcut to drawRoundRectComplex. + * @method rc + * @param {Number} x The horizontal coordinate to draw the round rect. + * @param {Number} y The vertical coordinate to draw the round rect. + * @param {Number} w The width of the round rect. + * @param {Number} h The height of the round rect. + * @param {Number} radiusTL Top left corner radius. + * @param {Number} radiusTR Top right corner radius. + * @param {Number} radiusBR Bottom right corner radius. + * @param {Number} radiusBL Bottom left corner radius. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.rc = p.drawRoundRectComplex; + + /** + * Shortcut to drawCircle. + * @method dc + * @param {Number} x x coordinate center point of circle. + * @param {Number} y y coordinate center point of circle. + * @param {Number} radius Radius of circle. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.dc = p.drawCircle; + + /** + * Shortcut to drawEllipse. + * @method de + * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} + * which draws from center. + * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} + * which draws from the center. + * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this + * number. + * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.de = p.drawEllipse; + + /** + * Shortcut to drawPolyStar. + * @method dp + * @param {Number} x Position of the center of the shape. + * @param {Number} y Position of the center of the shape. + * @param {Number} radius The outer radius of the shape. + * @param {Number} sides The number of points on the star or sides on the polygon. + * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular + * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy. + * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point + * directly to the right of the center. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.dp = p.drawPolyStar; + + /** + * Shortcut to decodePath. + * @method p + * @param {String} str The path string to decode. + * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.) + * @chainable + * @protected + **/ + p.p = p.decodePath; + + +// private methods: + /** + * @method _updateInstructions + * @param commit + * @protected + **/ + p._updateInstructions = function(commit) { + var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex; + + if (this._dirty && active.length) { + instr.length = commitIndex; // remove old, uncommitted commands + instr.push(Graphics.beginCmd); + + var l = active.length, ll = instr.length; + instr.length = ll+l; + for (var i=0; i= 2) { + var o = this.style = Graphics._ctx.createPattern(image, repetition || ""); + o.props = {image: image, repetition: repetition, type: "bitmap"}; + } + return this; + }; + p.path = false; + + /** + * Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class Stroke + * @constructor + * @param {Object} style A valid Context2D fillStyle. + * @param {Boolean} ignoreScale + **/ + /** + * A valid Context2D strokeStyle. + * @property style + * @type Object + */ + /** + * @property ignoreScale + * @type Boolean + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + p = (G.Stroke = function(style, ignoreScale) { + this.style = style; + this.ignoreScale = ignoreScale; + }).prototype; + p.exec = function(ctx) { + if (!this.style) { return; } + ctx.strokeStyle = this.style; + if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); } + ctx.stroke(); + if (this.ignoreScale) { ctx.restore(); } + }; + /** + * Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. + * See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information. + * @method linearGradient + * @param {Array} colors + * @param {Array} ratios + * @param {Number} x0 + * @param {Number} y0 + * @param {Number} x1 + * @param {Number} y1 + * @return {Fill} Returns this Stroke object for chaining or assignment. + */ + p.linearGradient = G.Fill.prototype.linearGradient; + /** + * Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. + * See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information. + * @method radialGradient + * @param {Array} colors + * @param {Array} ratios + * @param {Number} x0 + * @param {Number} y0 + * @param {Number} r0 + * @param {Number} x1 + * @param {Number} y1 + * @param {Number} r1 + * @return {Fill} Returns this Stroke object for chaining or assignment. + */ + p.radialGradient = G.Fill.prototype.radialGradient; + /** + * Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}. + * See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information. + * @method bitmap + * @param {HTMLImageElement} image + * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat. + * @return {Fill} Returns this Stroke object for chaining or assignment. + */ + p.bitmap = G.Fill.prototype.bitmap; + p.path = false; + + /** + * Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class StrokeStyle + * @constructor + * @param {Number} width + * @param {String} [caps=butt] + * @param {String} [joints=miter] + * @param {Number} [miterLimit=10] + * @param {Boolean} [ignoreScale=false] + **/ + /** + * @property width + * @type Number + */ + /** + * One of: butt, round, square + * @property caps + * @type String + */ + /** + * One of: round, bevel, miter + * @property joints + * @type String + */ + /** + * @property miterLimit + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + p = (G.StrokeStyle = function(width, caps, joints, miterLimit, ignoreScale) { + this.width = width; + this.caps = caps; + this.joints = joints; + this.miterLimit = miterLimit; + this.ignoreScale = ignoreScale; + }).prototype; + p.exec = function(ctx) { + ctx.lineWidth = (this.width == null ? "1" : this.width); + ctx.lineCap = (this.caps == null ? "butt" : (isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps])); + ctx.lineJoin = (this.joints == null ? "miter" : (isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints])); + ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit); + ctx.ignoreScale = (this.ignoreScale == null ? false : this.ignoreScale); + }; + p.path = false; + + /** + * Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class StrokeDash + * @constructor + * @param {Array} [segments] + * @param {Number} [offset=0] + **/ + /** + * @property segments + * @type Array + */ + /** + * @property offset + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + (G.StrokeDash = function(segments, offset) { + this.segments = segments; + this.offset = offset||0; + }).prototype.exec = function(ctx) { + if (ctx.setLineDash) { // feature detection. + ctx.setLineDash(this.segments|| G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn. + ctx.lineDashOffset = this.offset||0; + } + }; + /** + * The default value for segments (ie. no dash). + * @property EMPTY_SEGMENTS + * @static + * @final + * @readonly + * @protected + * @type {Array} + **/ + G.StrokeDash.EMPTY_SEGMENTS = []; + + /** + * Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class RoundRect + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} h + * @param {Number} radiusTL + * @param {Number} radiusTR + * @param {Number} radiusBR + * @param {Number} radiusBL + **/ + /** + * @property x + * @type Number + */ + /** + * @property y + * @type Number + */ + /** + * @property w + * @type Number + */ + /** + * @property h + * @type Number + */ + /** + * @property radiusTL + * @type Number + */ + /** + * @property radiusTR + * @type Number + */ + /** + * @property radiusBR + * @type Number + */ + /** + * @property radiusBL + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + (G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) { + this.x = x; this.y = y; + this.w = w; this.h = h; + this.radiusTL = radiusTL; this.radiusTR = radiusTR; + this.radiusBR = radiusBR; this.radiusBL = radiusBL; + }).prototype.exec = function(ctx) { + var max = (w max) { rTL = max; } + if (rTR < 0) { rTR *= (mTR=-1); } + if (rTR > max) { rTR = max; } + if (rBR < 0) { rBR *= (mBR=-1); } + if (rBR > max) { rBR = max; } + if (rBL < 0) { rBL *= (mBL=-1); } + if (rBL > max) { rBL = max; } + + ctx.moveTo(x+w-rTR, y); + ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR); + ctx.lineTo(x+w, y+h-rBR); + ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR); + ctx.lineTo(x+rBL, y+h); + ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL); + ctx.lineTo(x, y+rTL); + ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL); + ctx.closePath(); + }; + + /** + * Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class Circle + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} radius + **/ + /** + * @property x + * @type Number + */ + /** + * @property y + * @type Number + */ + /** + * @property radius + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + (G.Circle = function(x, y, radius) { + this.x = x; this.y = y; + this.radius = radius; + }).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); }; + + /** + * Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class Ellipse + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} h + **/ + /** + * @property x + * @type Number + */ + /** + * @property y + * @type Number + */ + /** + * @property w + * @type Number + */ + /** + * @property h + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + (G.Ellipse = function(x, y, w, h) { + this.x = x; this.y = y; + this.w = w; this.h = h; + }).prototype.exec = function(ctx) { + var x = this.x, y = this.y; + var w = this.w, h = this.h; + + var k = 0.5522848; + var ox = (w / 2) * k; + var oy = (h / 2) * k; + var xe = x + w; + var ye = y + h; + var xm = x + w / 2; + var ym = y + h / 2; + + ctx.moveTo(x, ym); + ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y); + ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym); + ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye); + ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym); + }; + + /** + * Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. + * @class PolyStar + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @param {Number} sides + * @param {Number} pointSize + * @param {Number} angle + **/ + /** + * @property x + * @type Number + */ + /** + * @property y + * @type Number + */ + /** + * @property radius + * @type Number + */ + /** + * @property sides + * @type Number + */ + /** + * @property pointSize + * @type Number + */ + /** + * @property angle + * @type Number + */ + /** + * Execute the Graphics command in the provided Canvas context. + * @method exec + * @param {CanvasRenderingContext2D} ctx The canvas rendering context + */ + (G.PolyStar = function(x, y, radius, sides, pointSize, angle) { + this.x = x; this.y = y; + this.radius = radius; + this.sides = sides; + this.pointSize = pointSize; + this.angle = angle; + }).prototype.exec = function(ctx) { + var x = this.x, y = this.y; + var radius = this.radius; + var angle = (this.angle||0)/180*Math.PI; + var sides = this.sides; + var ps = 1-(this.pointSize||0); + var a = Math.PI/sides; + + ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius); + for (var i=0; iNote: In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please + * check out the latest NEXT version in GitHub for an updated version with this issue resolved. The fix will be + * provided in the next release of EaselJS. + * @property mouseEnabled + * @type {Boolean} + * @default true + **/ + this.mouseEnabled = true; + + /** + * If false, the tick will not run on this display object (or its children). This can provide some performance benefits. + * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates + * on some display objects (ex. Sprite & MovieClip frame advancing, and DOMElement display properties). + * @property tickEnabled + * @type Boolean + * @default true + **/ + this.tickEnabled = true; + + /** + * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for + * debugging. + * @property name + * @type {String} + * @default null + **/ + this.name = null; + + /** + * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that + * contains this display object, or null if it has not been added + * to one. + * @property parent + * @final + * @type {Container} + * @default null + * @readonly + **/ + this.parent = null; + + /** + * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate + * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50. + * Cached object's registration points should be set based on pre-cache conditions, not cached size. + * @property regX + * @type {Number} + * @default 0 + **/ + this.regX = 0; + + /** + * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around + * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50. + * Cached object's registration points should be set based on pre-cache conditions, not cached size. + * @property regY + * @type {Number} + * @default 0 + **/ + this.regY = 0; + + /** + * The rotation in degrees for this display object. + * @property rotation + * @type {Number} + * @default 0 + **/ + this.rotation = 0; + + /** + * The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display + * object to twice its nominal width. To horizontally flip an object, set the scale to a negative number. + * @property scaleX + * @type {Number} + * @default 1 + **/ + this.scaleX = 1; + + /** + * The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display + * object to half its nominal height. To vertically flip an object, set the scale to a negative number. + * @property scaleY + * @type {Number} + * @default 1 + **/ + this.scaleY = 1; + + /** + * The factor to skew this display object horizontally. + * @property skewX + * @type {Number} + * @default 0 + **/ + this.skewX = 0; + + /** + * The factor to skew this display object vertically. + * @property skewY + * @type {Number} + * @default 0 + **/ + this.skewY = 0; + + /** + * A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If + * null, this property is inherited from the parent container. + * @property shadow + * @type {Shadow} + * @default null + **/ + this.shadow = null; + + /** + * Indicates whether this display object should be rendered to the canvas and included when running the Stage + * {{#crossLink "Stage/getObjectsUnderPoint"}}{{/crossLink}} method. + * @property visible + * @type {Boolean} + * @default true + **/ + this.visible = true; + + /** + * The x (horizontal) position of the display object, relative to its parent. + * @property x + * @type {Number} + * @default 0 + **/ + this.x = 0; + + /** The y (vertical) position of the display object, relative to its parent. + * @property y + * @type {Number} + * @default 0 + **/ + this.y = 0; + + /** + * If set, defines the transformation for this display object, overriding all other transformation properties + * (x, y, rotation, scale, skew). + * @property transformMatrix + * @type {Matrix2D} + * @default null + **/ + this.transformMatrix = null; + + /** + * The composite operation indicates how the pixels of this display object will be composited with the elements + * behind it. If `null`, this property is inherited from the parent container. For more information, read the + * + * whatwg spec on compositing. For a list of supported compositeOperation value, visit + * the W3C draft on Compositing and Blending. + * @property compositeOperation + * @type {String} + * @default null + **/ + this.compositeOperation = null; + + /** + * Indicates whether the display object should be drawn to a whole pixel when + * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole + * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true). + * @property snapToPixel + * @type {Boolean} + * @default true + **/ + this.snapToPixel = true; + + /** + * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}} + * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is + * cached. + * @property filters + * @type {Array} + * @default null + **/ + this.filters = null; + + /** + * A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation + * will be applied relative to the display object's parent coordinates (as if it were a child of the parent). + * @property mask + * @type {Shape} + * @default null + */ + this.mask = null; + + /** + * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}. + * The hit area will have its transformation applied relative to this display object's coordinate space (as though + * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested + * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's + * ancestors (parents). + * + * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events. + * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false. + * + * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}. + * @property hitArea + * @type {DisplayObject} + * @default null + */ + this.hitArea = null; + + /** + * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display + * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to + * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants. + * @property cursor + * @type {String} + * @default null + */ + this.cursor = null; + + + // private properties: + /** + * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} + * @property _cacheScale + * @protected + * @type {Number} + * @default 1 + * @deprecated + **/ + + /** + * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} + * @property _cacheDataURLID + * @protected + * @type {Number} + * @default 0 + * @deprecated + */ + + /** + * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} + * @property _cacheDataURL + * @protected + * @type {String} + * @default null + * @deprecated + */ + + /** + * @property _props + * @protected + * @type {DisplayObject} + * @default null + **/ + this._props = new createjs.DisplayProps(); + + /** + * @property _rectangle + * @protected + * @type {Rectangle} + * @default null + **/ + this._rectangle = new createjs.Rectangle(); + + /** + * @property _bounds + * @protected + * @type {Rectangle} + * @default null + **/ + this._bounds = null; + + /** + * Where StageGL should look for required display properties, matters only for leaf display objects. Containers + * or cached objects won't use this property, it's for native display of terminal elements. + * @property _webGLRenderStyle + * @protected + * @type {number} + * @default 0 + */ + this._webGLRenderStyle = DisplayObject._StageGL_NONE; + } + var p = createjs.extend(DisplayObject, createjs.EventDispatcher); + +// static properties: + /** + * Listing of mouse event names. Used in _hasMouseEventListener. + * @property _MOUSE_EVENTS + * @protected + * @static + * @type {Array} + **/ + DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"]; + + /** + * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}} + * with cross domain content. + * @property suppressCrossDomainErrors + * @static + * @type {Boolean} + * @default false + **/ + DisplayObject.suppressCrossDomainErrors = false; + + /** + * @property _snapToPixelEnabled + * @protected + * @static + * @type {Boolean} + * @default false + **/ + DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access. + + /** + * Enum like property for determining StageGL render lookup, i.e. where to expect properties. + * @property _StageGL_NONE + * @protected + * @static + * @type {number} + */ + DisplayObject._StageGL_NONE = 0; + + /** + * Enum like property for determining StageGL render lookup, i.e. where to expect properties. + * @property _StageGL_SPRITE + * @protected + * @static + * @type {number} + */ + DisplayObject._StageGL_SPRITE = 1; + + /** + * Enum like property for determining StageGL render lookup, i.e. where to expect properties. + * @property _StageGL_BITMAP + * @protected + * @static + * @type {number} + */ + DisplayObject._StageGL_BITMAP = 2; + + /** + * @property _hitTestCanvas + * @type {HTMLCanvasElement | Object} + * @static + * @protected + **/ + /** + * @property _hitTestContext + * @type {CanvasRenderingContext2D} + * @static + * @protected + **/ + var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas. + if (canvas.getContext) { + DisplayObject._hitTestCanvas = canvas; + DisplayObject._hitTestContext = canvas.getContext("2d"); + canvas.width = canvas.height = 1; + } + +// events: + /** + * Dispatched when the user presses their left mouse button over the display object. See the + * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event mousedown + * @since 0.6.0 + */ + + /** + * Dispatched when the user presses their left mouse button and then releases it while over the display object. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event click + * @since 0.6.0 + */ + + /** + * Dispatched when the user double clicks their left mouse button over this display object. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event dblclick + * @since 0.6.0 + */ + + /** + * Dispatched when the user's mouse enters this display object. This event must be enabled using + * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event mouseover + * @since 0.6.0 + */ + + /** + * Dispatched when the user's mouse leaves this display object. This event must be enabled using + * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event mouseout + * @since 0.6.0 + */ + + /** + * This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following + * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an + * aggregate of their content. + * + * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over + * shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on + * myContainer, two events would be received, each targeting a child element:
    + *
  1. when the mouse enters shapeA (target=shapeA)
  2. + *
  3. when the mouse enters shapeB (target=shapeB)
  4. + *
+ * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters + * the aggregate myContainer content (target=myContainer). + * + * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event rollover + * @since 0.7.0 + */ + + /** + * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following + * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an + * aggregate of their content. + * + * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over + * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}} + * on myContainer, two events would be received, each targeting a child element:
    + *
  1. when the mouse leaves shapeA (target=shapeA)
  2. + *
  3. when the mouse leaves shapeB (target=shapeB)
  4. + *
+ * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves + * the aggregate myContainer content (target=myContainer). + * + * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event rollout + * @since 0.7.0 + */ + + /** + * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove + * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be + * useful for dragging and similar operations. + * + * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed + * (e.g. during a `pressmove` event), a `pressmove` event is still generated. However since it is no longer in the + * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive + * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressup:event"}}{{/crossLink}}` + * or `pressmove` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), + * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. + * @event pressmove + * @since 0.7.0 + */ + + /** + * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event + * will be generated on that object when that mouse press is released. This can be useful for dragging and similar + * operations. + * + * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed + * (e.g. during a `pressmove` event), a `pressup` event is still generated. However since it is no longer in the + * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive + * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressmove:event"}}{{/crossLink}}` + * or `pressup` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), + * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. + * @event pressup + * @since 0.7.0 + */ + + /** + * Dispatched when the display object is added to a parent container. + * @event added + */ + + /** + * Dispatched when the display object is removed from its parent container. + * @event removed + */ + + /** + * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the + * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on + * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their + * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being + * dispatched on their parent. + * @event tick + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For + * example if you called stage.update("hello"), then the params would be ["hello"]. + * @since 0.6.0 + */ + + +// getter / setters: + /** + * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead. + * @method _getStage + * @protected + * @return {Stage} + **/ + p._getStage = function() { + // uses dynamic access to avoid circular dependencies; + var o = this, _Stage = createjs["Stage"]; + while (o.parent) { o = o.parent; } + if (o instanceof _Stage) { return o; } + return null; + }; + // DisplayObject.getStage is @deprecated. Remove for 1.1+ + p.getStage = createjs.deprecate(p._getStage, "DisplayObject.getStage"); + + /** + * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one. + * @property stage + * @type {Stage} + * @readonly + **/ + + /** + * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to + * determine if the cache has changed since a previous check. + * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} + * @property cacheID + * @deprecated + * @type {Number} + * @default 0 + */ + + /** + * Set both the {{#crossLink "DisplayObject/scaleX:property"}}{{/crossLink}} and the {{#crossLink "DisplayObject/scaleY"}}{{/crossLink}} + * property to the same value. Note that when you get the value, if the `scaleX` and `scaleY` are different values, + * it will return only the `scaleX`. + * @property scaleX + * @type {Number} + * @default 1 + */ + try { + Object.defineProperties(p, { + stage: { get: p._getStage }, + cacheID: { + get: function(){ return this.bitmapCache && this.bitmapCache.cacheID }, + set: function(a){ this.bitmapCache && (this.bitmapCache.cacheID = a) } + }, + scale: { + get: function() { return this.scaleX; }, + set: function(scale) { this.scaleX = this.scaleY = scale; }, + } + }); + } catch (e) {} + + +// public methods: + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0); + }; + + /** + * Draws the display object 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). + * @return {Boolean} + **/ + p.draw = function(ctx, ignoreCache) { + var cache = this.bitmapCache; + if(cache && !ignoreCache) { + return cache.draw(ctx); + } + return false; + }; + + /** + * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow + * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}. + * @method updateContext + * @param {CanvasRenderingContext2D} ctx The canvas 2D to update. + **/ + p.updateContext = function(ctx) { + var o=this, mask=o.mask, mtx= o._props.matrix; + + if (mask && mask.graphics && !mask.graphics.isEmpty()) { + mask.getMatrix(mtx); + ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); + + mask.graphics.drawAsPath(ctx); + ctx.clip(); + + mtx.invert(); + ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); + } + + this.getMatrix(mtx); + var tx = mtx.tx, ty = mtx.ty; + if (DisplayObject._snapToPixelEnabled && o.snapToPixel) { + tx = tx + (tx < 0 ? -0.5 : 0.5) | 0; + ty = ty + (ty < 0 ? -0.5 : 0.5) | 0; + } + ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty); + ctx.globalAlpha *= o.alpha; + if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; } + if (o.shadow) { this._applyShadow(ctx, o.shadow); } + }; + + /** + * Draws the display object into a new element, which is then used for subsequent draws. Intended for complex content + * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape), + * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The + * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually + * update the cache by calling updateCache() again. You must specify the cached area via the x, y, w, + * and h parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates. + * + *

Example

+ * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25: + * + * var shape = new createjs.Shape(); + * shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25); + * shape.cache(-25, -25, 50, 50); + * + * Note that filters need to be defined before the cache is applied or you will have to call updateCache after + * application. Check out the {{#crossLink "Filter"}}{{/crossLink}} class for more information. Some filters + * (ex. BlurFilter) may not work as expected in conjunction with the scale param. + * + * Usually, the resulting cacheCanvas will have the dimensions width * scale, height * scale, however some filters (ex. BlurFilter) + * will add padding to the canvas dimensions. + * + * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. + * This allows for easier interaction and alternate cache methods like WebGL with {{#crossLink "StageGL"}}{{/crossLink}}. + * For more information on the options object, see the BitmapCache {{#crossLink "BitmapCache/define"}}{{/crossLink}}. + * + * @method cache + * @param {Number} x The x coordinate origin for the cache region. + * @param {Number} y The y coordinate origin for the cache region. + * @param {Number} width The width of the cache region. + * @param {Number} height The height of the cache region. + * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using + * myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate + * cached elements with greater fidelity. Default is 1. + * @param {Object} [options=undefined] Specify additional parameters for the cache logic + **/ + p.cache = function(x, y, width, height, scale, options) { + if(!this.bitmapCache){ + this.bitmapCache = new createjs.BitmapCache(); + } + this.bitmapCache.define(this, x, y, width, height, scale, options); + }; + + /** + * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error. + * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object + * will be drawn over the existing cache using the specified compositeOperation. + * + *

Example

+ * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line + * will be drawn on top of the old one. + * + * // Not shown: Creating the shape, and caching it. + * shapeInstance.clear(); + * shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200); + * shapeInstance.updateCache(); + * + * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. + * This allows for easier interaction and alternate cache methods like WebGL and {{#crossLink "StageGL"}}{{/crossLink}}. + * + * @method updateCache + * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it. + * + * whatwg spec on compositing. + **/ + p.updateCache = function(compositeOperation) { + if(!this.bitmapCache) { + throw "cache() must be called before updateCache()"; + } + this.bitmapCache.update(compositeOperation); + }; + + /** + * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information. + * @method uncache + **/ + p.uncache = function() { + if(this.bitmapCache) { + this.bitmapCache.release(); + this.bitmapCache = undefined; + } + }; + + /** + * Returns a data URL for the cache, or null if this display object is not cached. + * Only generated if the cache has changed, otherwise returns last result. + * @method getCacheDataURL + * @return {String} The image data url for the cache. + **/ + p.getCacheDataURL = function() { + return this.bitmapCache?this.bitmapCache.getCacheDataURL():null; + }; + + /** + * Transforms the specified x and y position from the coordinate space of the display object + * to the global (stage) coordinate space. For example, this could be used to position an HTML label + * over a specific point on a nested display object. Returns a Point instance with x and y properties + * correlating to the transformed coordinates on the stage. + * + *

Example

+ * + * displayObject.x = 300; + * displayObject.y = 200; + * stage.addChild(displayObject); + * var point = displayObject.localToGlobal(100, 100); + * // Results in x=400, y=300 + * + * @method localToGlobal + * @param {Number} x The x position in the source display object to transform. + * @param {Number} y The y position in the source display object to transform. + * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. + * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates + * on the stage. + **/ + p.localToGlobal = function(x, y, pt) { + return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point()); + }; + + /** + * Transforms the specified x and y position from the global (stage) coordinate space to the + * coordinate space of the display object. For example, this could be used to determine + * the current mouse position within the display object. Returns a Point instance with x and y properties + * correlating to the transformed position in the display object's coordinate space. + * + *

Example

+ * + * displayObject.x = 300; + * displayObject.y = 200; + * stage.addChild(displayObject); + * var point = displayObject.globalToLocal(100, 100); + * // Results in x=-200, y=-100 + * + * @method globalToLocal + * @param {Number} x The x position on the stage to transform. + * @param {Number} y The y position on the stage to transform. + * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. + * @return {Point} A Point instance with x and y properties correlating to the transformed position in the + * display object's coordinate space. + **/ + p.globalToLocal = function(x, y, pt) { + return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point()); + }; + + /** + * Transforms the specified x and y position from the coordinate space of this display object to the coordinate + * space of the target display object. Returns a Point instance with x and y properties correlating to the + * transformed position in the target's coordinate space. Effectively the same as using the following code with + * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. + * + * var pt = this.localToGlobal(x, y); + * pt = target.globalToLocal(pt.x, pt.y); + * + * @method localToLocal + * @param {Number} x The x position in the source display object to transform. + * @param {Number} y The y position on the source display object to transform. + * @param {DisplayObject} target The target display object to which the coordinates will be transformed. + * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. + * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position + * in the target's coordinate space. + **/ + p.localToLocal = function(x, y, target, pt) { + pt = this.localToGlobal(x, y, pt); + return target.globalToLocal(pt.x, pt.y, pt); + }; + + /** + * Shortcut method to quickly set the transform properties on the display object. All parameters are optional. + * Omitted parameters will have the default value set. + * + *

Example

+ * + * displayObject.setTransform(100, 100, 2, 2); + * + * @method setTransform + * @param {Number} [x=0] The horizontal translation (x position) in pixels + * @param {Number} [y=0] The vertical translation (y position) in pixels + * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1 + * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1 + * @param {Number} [rotation=0] The rotation, in degrees + * @param {Number} [skewX=0] The horizontal skew factor + * @param {Number} [skewY=0] The vertical skew factor + * @param {Number} [regX=0] The horizontal registration point in pixels + * @param {Number} [regY=0] The vertical registration point in pixels + * @return {DisplayObject} Returns this instance. Useful for chaining commands. + * @chainable + */ + p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { + this.x = x || 0; + this.y = y || 0; + this.scaleX = scaleX == null ? 1 : scaleX; + this.scaleY = scaleY == null ? 1 : scaleY; + this.rotation = rotation || 0; + this.skewX = skewX || 0; + this.skewY = skewY || 0; + this.regX = regX || 0; + this.regY = regY || 0; + return this; + }; + + /** + * Returns a matrix based on this object's current transform. + * @method getMatrix + * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new + * Matrix object is returned. + * @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); + }; + + /** + * Generates a Matrix2D object representing the combined transform of the display object and all of its + * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can + * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} + * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. + * @method getConcatenatedMatrix + * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values. + * If null, a new Matrix2D object is returned. + * @return {Matrix2D} The combined matrix. + **/ + p.getConcatenatedMatrix = function(matrix) { + var o = this, mtx = this.getMatrix(matrix); + while (o = o.parent) { + mtx.prependMatrix(o.getMatrix(o._props.matrix)); + } + return mtx; + }; + + /** + * Generates a DisplayProps object representing the combined display properties of the object and all of its + * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). + * @method getConcatenatedDisplayProps + * @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values. + * If null, a new DisplayProps object is returned. + * @return {DisplayProps} The combined display properties. + **/ + p.getConcatenatedDisplayProps = function(props) { + props = props ? props.identity() : new createjs.DisplayProps(); + var o = this, mtx = o.getMatrix(props.matrix); + do { + props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation); + + // we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param. + // this could be simplified (ie. just done as part of the prepend above) if we switched to using a pool. + if (o != this) { mtx.prependMatrix(o.getMatrix(o._props.matrix)); } + } while (o = o.parent); + return props; + }; + + /** + * Tests whether the display object intersects the specified point in local coordinates (ie. draws a pixel with alpha > 0 at + * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object. + * + *

Example

+ * + * stage.addEventListener("stagemousedown", handleMouseDown); + * function handleMouseDown(event) { + * var hit = myShape.hitTest(event.stageX, event.stageY); + * } + * + * Please note that shape-to-shape collision is not currently supported by EaselJS. + * @method hitTest + * @param {Number} x The x position to check in the display object's local coordinates. + * @param {Number} y The y position to check in the display object's local coordinates. + * @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified + * local Point. + */ + p.hitTest = function(x, y) { + var ctx = DisplayObject._hitTestContext; + ctx.setTransform(1, 0, 0, 1, -x, -y); + this.draw(ctx); + + var hit = this._testHit(ctx); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, 2, 2); + return hit; + }; + + /** + * Provides a chainable shortcut method for setting a number of properties on the instance. + * + *

Example

+ * + * var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25); + * var shape = stage.addChild(new Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5}); + * + * @method set + * @param {Object} props A generic object containing properties to copy to the DisplayObject instance. + * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.) + * @chainable + */ + p.set = function(props) { + for (var n in props) { this[n] = props[n]; } + return this; + }; + + /** + * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation). + * Objects that have been cached will return the bounds of the cache. + * + * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use + * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container + * bounds. + * + * + * + * + * + * + * + * + * + *
All + * All display objects support setting bounds manually using setBounds(). Likewise, display objects that + * have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override + * the automatic calculations listed below. + *
Bitmap + * Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0). + *
Sprite + * Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified + * in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} + *
Container + * Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds(). + *
Shape + * Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds. + *
Text + * Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are + * not, especially when using textBaseline values other than "top". + *
BitmapText + * Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close + * to (x=0,y=0). + *
+ * + * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and + * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the + * bounds explicitly: + * + * var bounds = obj.getBounds(); + * obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); + * // getBounds will now use the set values, instead of recalculating + * + * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its + * values if you need to retain it. + * + * var myBounds = obj.getBounds().clone(); + * // OR: + * myRect.copy(obj.getBounds()); + * + * @method getBounds + * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this + * object. + **/ + p.getBounds = function() { + if (this._bounds) { return this._rectangle.copy(this._bounds); } + var cacheCanvas = this.cacheCanvas; + if (cacheCanvas) { + var scale = this._cacheScale; + return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale); + } + return null; + }; + + /** + * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied). + * Objects that have been cached will return the transformed bounds of the cache. + * + * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use + * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container + * bounds. + * + * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its + * values if you need to retain it. + * + * Container instances calculate aggregate bounds for all children that return bounds via getBounds. + * @method getTransformedBounds + * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object. + **/ + p.getTransformedBounds = function() { + return this._getBounds(); + }; + + /** + * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape & + * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always + * override calculated bounds. + * + * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance + * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50). + * @method setBounds + * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds. + * @param {Number} y The y origin of the bounds. + * @param {Number} width The width of the bounds. + * @param {Number} height The height of the bounds. + **/ + p.setBounds = function(x, y, width, height) { + if (x == null) { this._bounds = x; return; } + this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height); + }; + + /** + * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are + * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements + * are copied by reference (masks, individual filter instances, hit area) + * @method clone + * @return {DisplayObject} A clone of the current DisplayObject instance. + **/ + p.clone = function() { + return this._cloneProps(new DisplayObject()); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[DisplayObject (name="+ this.name +")]"; + }; + + +// private methods: + /** + * Called before the object gets drawn and is a chance to ensure the display state of the object is correct. + * Mostly used by {{#crossLink "MovieClip"}}{{/crossLink}} and {{#crossLink "BitmapText"}}{{/crossLink}} to + * correct their internal state and children prior to being drawn. + * + * Is manually called via draw in a {{#crossLink "Stage"}}{{/crossLink}} but is automatically called when + * present in a {{#crossLink "StageGL"}}{{/crossLink}} instance. + * + * @method _updateState + * @default null + */ + p._updateState = null; + + // separated so it can be used more easily in subclasses: + /** + * @method _cloneProps + * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject + * instance copied into. + * @return {DisplayObject} o + * @protected + **/ + p._cloneProps = function(o) { + o.alpha = this.alpha; + o.mouseEnabled = this.mouseEnabled; + o.tickEnabled = this.tickEnabled; + o.name = this.name; + o.regX = this.regX; + o.regY = this.regY; + o.rotation = this.rotation; + o.scaleX = this.scaleX; + o.scaleY = this.scaleY; + o.shadow = this.shadow; + o.skewX = this.skewX; + o.skewY = this.skewY; + o.visible = this.visible; + o.x = this.x; + o.y = this.y; + o.compositeOperation = this.compositeOperation; + o.snapToPixel = this.snapToPixel; + o.filters = this.filters==null?null:this.filters.slice(0); + o.mask = this.mask; + o.hitArea = this.hitArea; + o.cursor = this.cursor; + o._bounds = this._bounds; + return o; + }; + + /** + * @method _applyShadow + * @protected + * @param {CanvasRenderingContext2D} ctx + * @param {Shadow} shadow + **/ + p._applyShadow = function(ctx, shadow) { + shadow = shadow || Shadow.identity; + ctx.shadowColor = shadow.color; + ctx.shadowOffsetX = shadow.offsetX; + ctx.shadowOffsetY = shadow.offsetY; + ctx.shadowBlur = shadow.blur; + }; + + /** + * @method _tick + * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. + * @protected + **/ + p._tick = function(evtObj) { + // because tick can be really performance sensitive, check for listeners before calling dispatchEvent. + var ls = this._listeners; + if (ls && ls["tick"]) { + // reset & reuse the event object to avoid construction / GC costs: + evtObj.target = null; + evtObj.propagationStopped = evtObj.immediatePropagationStopped = false; + this.dispatchEvent(evtObj); + } + }; + + /** + * @method _testHit + * @protected + * @param {CanvasRenderingContext2D} ctx + * @return {Boolean} + **/ + p._testHit = function(ctx) { + try { + var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1; + } catch (e) { + if (!DisplayObject.suppressCrossDomainErrors) { + throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images."; + } + } + return hit; + }; + + /** + * @method _getBounds + * @param {Matrix2D} matrix + * @param {Boolean} ignoreTransform If true, does not apply this object's transform. + * @return {Rectangle} + * @protected + **/ + p._getBounds = function(matrix, ignoreTransform){ + return this._transformBounds(this.getBounds(), matrix, ignoreTransform); + }; + + /** + * @method _transformBounds + * @param {Rectangle} bounds + * @param {Matrix2D} matrix + * @param {Boolean} ignoreTransform + * @return {Rectangle} + * @protected + **/ + p._transformBounds = function(bounds, matrix, ignoreTransform) { + if (!bounds) { return bounds; } + var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix; + mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx); + + if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this. + if (matrix) { mtx.prependMatrix(matrix); } + + var x_a = width*mtx.a, x_b = width*mtx.b; + var y_c = height*mtx.c, y_d = height*mtx.d; + var tx = mtx.tx, ty = mtx.ty; + + var minX = tx, maxX = tx, minY = ty, maxY = ty; + + if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } + if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } + if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } + + if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } + if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } + if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } + + return bounds.setValues(minX, minY, maxX-minX, maxY-minY); + }; + + /** + * Indicates whether the display object has any mouse event listeners or a cursor. + * @method _isMouseOpaque + * @return {Boolean} + * @protected + **/ + p._hasMouseEventListener = function() { + var evts = DisplayObject._MOUSE_EVENTS; + for (var i= 0, l=evts.length; itransform and alpha properties concatenated with their parent + * Container. + * + * For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with x=50 + * and alpha=0.7 will be rendered to the canvas at x=150 and alpha=0.35. + * Containers have some overhead, so you generally shouldn't create a Container to hold a single child. + * + *

Example

+ * + * var container = new createjs.Container(); + * container.addChild(bitmapInstance, shapeInstance); + * container.x = 100; + * + * @class Container + * @extends DisplayObject + * @constructor + **/ + function Container() { + this.DisplayObject_constructor(); + + // public properties: + /** + * The array of children in the display list. You should usually use the child management methods such as + * {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}}, + * {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is + * included for advanced uses. + * @property children + * @type Array + * @default null + **/ + this.children = []; + + /** + * Indicates whether the children of this container are independently enabled for mouse/pointer interaction. + * If false, the children will be aggregated under the container - for example, a click on a child shape would + * trigger a click event on the container. + * @property mouseChildren + * @type Boolean + * @default true + **/ + this.mouseChildren = true; + + /** + * If false, the tick will not be propagated to children of this Container. This can provide some performance benefits. + * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates + * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling). + * @property tickChildren + * @type Boolean + * @default true + **/ + this.tickChildren = true; + } + var p = createjs.extend(Container, createjs.DisplayObject); + + +// getter / setters: + /** + * Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead. + * @method _getNumChildren + * @protected + * @return {Number} + **/ + p._getNumChildren = function() { + return this.children.length; + }; + // Container.getNumChildren is @deprecated. Remove for 1.1+ + p.getNumChildren = createjs.deprecate(p._getNumChildren, "Container.getNumChildren"); + + /** + * Returns the number of children in the container. + * @property numChildren + * @type {Number} + * @readonly + **/ + try { + Object.defineProperties(p, { + numChildren: { get: p._getNumChildren } + }); + } catch (e) {} + + +// public methods: + /** + * Constructor alias for backwards compatibility. This method will be removed in future versions. + * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. + * @method initialize + * @deprecated in favour of `createjs.promote()` + **/ + p.initialize = Container; // TODO: deprecated. + + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var hasContent = this.cacheCanvas || this.children.length; + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + }; + + /** + * Draws the display object 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) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + + // this ensures we don't have issues with display list changes that occur during a draw: + var list = this.children.slice(); + for (var i=0,l=list.length; iExample + * + * container.addChild(bitmapInstance); + * + * You can also add multiple children at once: + * + * container.addChild(bitmapInstance, shapeInstance, textInstance); + * + * @method addChild + * @param {DisplayObject} child The display object to add. + * @return {DisplayObject} The child that was added, or the last child if multiple children were added. + **/ + p.addChild = function(child) { + if (child == null) { return child; } + var l = arguments.length; + if (l > 1) { + for (var i=0; iExample + * + * addChildAt(child1, index); + * + * You can also add multiple children, such as: + * + * addChildAt(child1, child2, ..., index); + * + * The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list, + * you could use: + * + * container.addChildAt(myShape, container.getChildIndex(otherShape)); + * + * This would also bump otherShape's index up by one. Fails silently if the index is out of range. + * + * @method addChildAt + * @param {DisplayObject} child The display object to add. + * @param {Number} index The index to add the child at. + * @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added. + **/ + p.addChildAt = function(child, index) { + var l = arguments.length; + var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1] + if (indx < 0 || indx > this.children.length) { return arguments[l-2]; } + if (l > 2) { + for (var i=0; iExample + * + * container.removeChild(child); + * + * You can also remove multiple children: + * + * removeChild(child1, child2, ...); + * + * Returns true if the child (or children) was removed, or false if it was not in the display list. + * @method removeChild + * @param {DisplayObject} child The child to remove. + * @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list. + **/ + p.removeChild = function(child) { + var l = arguments.length; + if (l > 1) { + var good = true; + for (var i=0; iExample + * + * container.removeChildAt(2); + * + * You can also remove multiple children: + * + * container.removeChild(2, 7, ...) + * + * Returns true if the child (or children) was removed, or false if any index was out of range. + * @method removeChildAt + * @param {Number} index The index of the child to remove. + * @return {Boolean} true if the child (or children) was removed, or false if any index was out of range. + **/ + p.removeChildAt = function(index) { + var l = arguments.length; + if (l > 1) { + var a = []; + for (var i=0; iExample + * + * container.removeAllChildren(); + * + * @method removeAllChildren + **/ + p.removeAllChildren = function() { + var kids = this.children; + while (kids.length) { this._removeChildAt(0); } + }; + + /** + * Returns the child at the specified index. + * + *

Example

+ * + * container.getChildAt(2); + * + * @method getChildAt + * @param {Number} index The index of the child to return. + * @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index. + **/ + p.getChildAt = function(index) { + return this.children[index]; + }; + + /** + * Returns the child with the specified name. + * @method getChildByName + * @param {String} name The name of the child to return. + * @return {DisplayObject} The child with the specified name. + **/ + p.getChildByName = function(name) { + var kids = this.children; + for (var i=0,l=kids.length;iExample: Display children with a higher y in front. + * + * var sortFunction = function(obj1, obj2, options) { + * if (obj1.y > obj2.y) { return 1; } + * if (obj1.y < obj2.y) { return -1; } + * return 0; + * } + * container.sortChildren(sortFunction); + * + * @method sortChildren + * @param {Function} sortFunction the function to use to sort the child list. See JavaScript's Array.sort + * documentation for details. + **/ + p.sortChildren = function(sortFunction) { + this.children.sort(sortFunction); + }; + + /** + * Returns the index of the specified child in the display list, or -1 if it is not in the display list. + * + *

Example

+ * + * var index = container.getChildIndex(child); + * + * @method getChildIndex + * @param {DisplayObject} child The child to return the index of. + * @return {Number} The index of the specified child. -1 if the child is not found. + **/ + p.getChildIndex = function(child) { + return createjs.indexOf(this.children, child); + }; + + /** + * Swaps the children at the specified indexes. Fails silently if either index is out of range. + * @method swapChildrenAt + * @param {Number} index1 + * @param {Number} index2 + **/ + p.swapChildrenAt = function(index1, index2) { + var kids = this.children; + var o1 = kids[index1]; + var o2 = kids[index2]; + if (!o1 || !o2) { return; } + kids[index1] = o2; + kids[index2] = o1; + }; + + /** + * Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this + * Container. + * @method swapChildren + * @param {DisplayObject} child1 + * @param {DisplayObject} child2 + **/ + p.swapChildren = function(child1, child2) { + var kids = this.children; + var index1,index2; + for (var i=0,l=kids.length;i= l) { return; } + for (var i=0;i 0 at the + * specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all + * transform properties including regX/Y. + * @method hitTest + * @param {Number} x The x position to check in the display object's local coordinates. + * @param {Number} y The y position to check in the display object's local coordinates. + * @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified + * coordinates. + **/ + p.hitTest = function(x, y) { + // TODO: optimize to use the fast cache check where possible. + return (this.getObjectUnderPoint(x, y) != null); + }; + + /** + * Returns an array of all display objects under the specified coordinates that are in this container's display + * list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}} + * set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0. + * This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully. + * For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}), + * and only if the mouse's position has changed. + * + *
    + *
  • By default (mode=0) this method evaluates all display objects.
  • + *
  • By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}} + * and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.
  • + *
  • Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event + * listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects + * that would normally intercept mouse interaction will be included. This can significantly improve performance + * in some cases by reducing the number of display objects that need to be tested.
  • + * + * + * This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}. + * @method getObjectsUnderPoint + * @param {Number} x The x position in the container to test. + * @param {Number} y The y position in the container to test. + * @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects. + * @return {Array} An Array of DisplayObjects under the specified coordinates. + **/ + p.getObjectsUnderPoint = function(x, y, mode) { + var arr = []; + var pt = this.localToGlobal(x, y); + this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1); + return arr; + }; + + /** + * Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display + * object. This runs significantly faster than getObjectsUnderPoint(), but is still potentially an expensive + * operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information. + * @method getObjectUnderPoint + * @param {Number} x The x position in the container to test. + * @param {Number} y The y position in the container to test. + * @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects. + * @return {DisplayObject} The top-most display object under the specified coordinates. + **/ + p.getObjectUnderPoint = function(x, y, mode) { + var pt = this.localToGlobal(x, y); + return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1); + }; + + /** + * Docced in superclass. + */ + p.getBounds = function() { + return this._getBounds(null, true); + }; + + + /** + * Docced in superclass. + */ + p.getTransformedBounds = function() { + return this._getBounds(); + }; + + /** + * Returns a clone of this Container. Some properties that are specific to this instance's current context are + * reverted to their defaults (for example .parent). + * @method clone + * @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the + * properties of the container will be cloned, but the new instance will not have any children. + * @return {Container} A clone of the current Container instance. + **/ + p.clone = function(recursive) { + var o = this._cloneProps(new Container()); + if (recursive) { this._cloneChildren(o); } + return o; + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Container (name="+ this.name +")]"; + }; + + +// private methods: + /** + * @method _tick + * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. + * @protected + **/ + p._tick = function(evtObj) { + if (this.tickChildren) { + for (var i=this.children.length-1; i>=0; i--) { + var child = this.children[i]; + if (child.tickEnabled && child._tick) { child._tick(evtObj); } + } + } + this.DisplayObject__tick(evtObj); + }; + + /** + * Recursively clones all children of this container, and adds them to the target container. + * @method cloneChildren + * @protected + * @param {Container} o The target container. + **/ + p._cloneChildren = function(o) { + if (o.children.length) { o.removeAllChildren(); } + var arr = o.children; + for (var i=0, l=this.children.length; i this.children.length-1) { return false; } + var child = this.children[index]; + if (child) { child.parent = null; } + this.children.splice(index, 1); + if (!silent) { child.dispatchEvent("removed"); } + return true; + }; + + /** + * @method _getObjectsUnderPoint + * @param {Number} x + * @param {Number} y + * @param {Array} arr + * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners. + * @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object. + * @param {Number} currentDepth Indicates the current depth of the search. + * @return {DisplayObject} + * @protected + **/ + p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) { + currentDepth = currentDepth || 0; + if (!currentDepth && !this._testMask(this, x, y)) { return null; } + var mtx, ctx = createjs.DisplayObject._hitTestContext; + activeListener = activeListener || (mouse&&this._hasMouseEventListener()); + + // draw children one at a time, and check if we get a hit: + var children = this.children, l = children.length; + for (var i=l-1; i>=0; i--) { + var child = children[i]; + var hitArea = child.hitArea; + if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; } + if (!hitArea && !this._testMask(child, x, y)) { continue; } + + // if a child container has a hitArea then we only need to check its hitAre2a, so we can treat it as a normal DO: + if (!hitArea && child instanceof Container) { + var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1); + if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; } + } else { + if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; } + + // TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense. + var props = child.getConcatenatedDisplayProps(child._props); + mtx = props.matrix; + + if (hitArea) { + mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix)); + props.alpha = hitArea.alpha; + } + + ctx.globalAlpha = props.alpha; + ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y); + (hitArea||child).draw(ctx); + if (!this._testHit(ctx)) { continue; } + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, 2, 2); + if (arr) { arr.push(child); } + else { return (mouse && !this.mouseChildren) ? this : child; } + } + } + return null; + }; + + /** + * @method _testMask + * @param {DisplayObject} target + * @param {Number} x + * @param {Number} y + * @return {Boolean} Indicates whether the x/y is within the masked region. + * @protected + **/ + p._testMask = function(target, x, y) { + var mask = target.mask; + if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; } + + var mtx = this._props.matrix, parent = target.parent; + mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity(); + mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx); + + var ctx = createjs.DisplayObject._hitTestContext; + ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y); + + // draw the mask as a solid fill: + mask.graphics.drawAsPath(ctx); + ctx.fillStyle = "#000"; + ctx.fill(); + + if (!this._testHit(ctx)) { return false; } + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, 2, 2); + + return true; + }; + + /** + * @method _getBounds + * @param {Matrix2D} matrix + * @param {Boolean} ignoreTransform If true, does not apply this object's transform. + * @return {Rectangle} + * @protected + **/ + p._getBounds = function(matrix, ignoreTransform) { + var bounds = this.DisplayObject_getBounds(); + if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); } + + var mtx = this._props.matrix; + mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx); + if (matrix) { mtx.prependMatrix(matrix); } + + var l = this.children.length, rect=null; + for (var i=0; iExample + * This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child + * and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}. + * + * var stage = new createjs.Stage("canvasElementId"); + * var image = new createjs.Bitmap("imagePath.png"); + * stage.addChild(image); + * createjs.Ticker.addEventListener("tick", handleTick); + * function handleTick(event) { + * image.x += 10; + * stage.update(); + * } + * + * @class Stage + * @extends Container + * @constructor + * @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id + * of a canvas object in the current document. + **/ + function Stage(canvas) { + this.Container_constructor(); + + + // public properties: + /** + * Indicates whether the stage should automatically clear the canvas before each render. You can set this to false + * to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for + * example). + * + *

    Example

    + * + * var stage = new createjs.Stage("canvasId"); + * stage.autoClear = false; + * + * @property autoClear + * @type Boolean + * @default true + **/ + this.autoClear = true; + + /** + * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the + * first stage that will be ticked (or they will clear each other's render). + * + * When changing the canvas property you must disable the events on the old canvas, and enable events on the + * new canvas or mouse events will not work as expected. For example: + * + * myStage.enableDOMEvents(false); + * myStage.canvas = anotherCanvas; + * myStage.enableDOMEvents(true); + * + * @property canvas + * @type HTMLCanvasElement | Object + **/ + this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas; + + /** + * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent + * position over the canvas, and mouseInBounds will be set to false. + * @property mouseX + * @type Number + * @readonly + **/ + this.mouseX = 0; + + /** + * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent + * position over the canvas, and mouseInBounds will be set to false. + * @property mouseY + * @type Number + * @readonly + **/ + this.mouseY = 0; + + /** + * Specifies the area of the stage to affect when calling update. This can be use to selectively + * re-draw specific regions of the canvas. If null, the whole canvas area is drawn. + * @property drawRect + * @type {Rectangle} + */ + this.drawRect = null; + + /** + * Indicates whether display objects should be rendered on whole pixels. You can set the + * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of + * display objects to false to enable/disable this behaviour on a per instance basis. + * @property snapToPixelEnabled + * @type Boolean + * @default false + **/ + this.snapToPixelEnabled = false; + + /** + * Indicates whether the mouse is currently within the bounds of the canvas. + * @property mouseInBounds + * @type Boolean + * @default false + **/ + this.mouseInBounds = false; + + /** + * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas. + * @property tickOnUpdate + * @type Boolean + * @default true + **/ + this.tickOnUpdate = true; + + /** + * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See + * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}} + * x/y/rawX/rawY. + * @property mouseMoveOutside + * @type Boolean + * @default false + **/ + this.mouseMoveOutside = false; + + + /** + * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas. + * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas. + * @property preventSelection + * @type Boolean + * @default true + **/ + this.preventSelection = true; + + /** + * The hitArea property is not supported for Stage. + * @property hitArea + * @type {DisplayObject} + * @default null + */ + + + // private properties: + /** + * Holds objects with data for each active pointer id. Each object has the following properties: + * x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position) + * @property _pointerData + * @type {Object} + * @private + */ + this._pointerData = {}; + + /** + * Number of active pointers. + * @property _pointerCount + * @type {Object} + * @private + */ + this._pointerCount = 0; + + /** + * The ID of the primary pointer. + * @property _primaryPointerID + * @type {Object} + * @private + */ + this._primaryPointerID = null; + + /** + * @property _mouseOverIntervalID + * @protected + * @type Number + **/ + this._mouseOverIntervalID = null; + + /** + * @property _nextStage + * @protected + * @type Stage + **/ + this._nextStage = null; + + /** + * @property _prevStage + * @protected + * @type Stage + **/ + this._prevStage = null; + + + // initialize: + this.enableDOMEvents(true); + } + var p = createjs.extend(Stage, createjs.Container); + +// events: + /** + * Dispatched when the user moves the mouse over the canvas. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event stagemousemove + * @since 0.6.0 + */ + + /** + * Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}} + * class for a listing of event properties. + * @event stagemousedown + * @since 0.6.0 + */ + + /** + * Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers). + * You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds. + * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. + * @event stagemouseup + * @since 0.6.0 + */ + + /** + * Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false). + * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}} + * class for a listing of event properties. + * @event mouseleave + * @since 0.7.0 + */ + + /** + * Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true). + * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}} + * class for a listing of event properties. + * @event mouseenter + * @since 0.7.0 + */ + + /** + * Dispatched each update immediately before the tick event is propagated through the display list. + * You can call preventDefault on the event object to cancel propagating the tick event. + * @event tickstart + * @since 0.7.0 + */ + + /** + * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if + * tickOnUpdate is false. Precedes the "drawstart" event. + * @event tickend + * @since 0.7.0 + */ + + /** + * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. + * You can call preventDefault on the event object to cancel the draw. + * @event drawstart + * @since 0.7.0 + */ + + /** + * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored. + * @event drawend + * @since 0.7.0 + */ + + +// getter / setters: + /** + * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them. + * This can be useful in cases where you have multiple layered canvases and want user interactions + * events to pass through. For example, this would relay mouse events from topStage to bottomStage: + * + * topStage.nextStage = bottomStage; + * + * To disable relaying, set nextStage to null. + * + * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings + * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled. + * Considerations when using roll over in relay targets:
      + *
    1. The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)
    2. + *
    3. All stages that wish to participate in mouse over interaction must enable them via enableMouseOver
    4. + *
    5. All relay targets will share the frequency value of the top-most stage
    6. + *
    + * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing + * 30 as it's desired frequency): + * topStage.nextStage = targetStage; + * topStage.enableMouseOver(10); + * targetStage.enableMouseOver(30); + * + * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its + * DOM events using: + * + * targetStage.enableDOMEvents(false); + * + * @property nextStage + * @type {Stage} + **/ + p._get_nextStage = function() { + return this._nextStage; + }; + p._set_nextStage = function(value) { + if (this._nextStage) { this._nextStage._prevStage = null; } + if (value) { value._prevStage = this; } + this._nextStage = value; + }; + + try { + Object.defineProperties(p, { + nextStage: { get: p._get_nextStage, set: p._set_nextStage } + }); + } catch (e) {} // TODO: use Log + + +// public methods: + /** + * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}} + * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false, + * and then render the display list to the canvas. + * + * @method update + * @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property. + **/ + p.update = function(props) { + if (!this.canvas) { return; } + if (this.tickOnUpdate) { this.tick(props); } + if (this.dispatchEvent("drawstart", false, true) === false) { return; } + createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled; + var r = this.drawRect, ctx = this.canvas.getContext("2d"); + ctx.setTransform(1, 0, 0, 1, 0, 0); + if (this.autoClear) { + if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); } + else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); } + } + ctx.save(); + if (this.drawRect) { + ctx.beginPath(); + ctx.rect(r.x, r.y, r.width, r.height); + ctx.clip(); + } + this.updateContext(ctx); + this.draw(ctx, false); + ctx.restore(); + this.dispatchEvent("drawend"); + }; + + /** + * 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. + * + * If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is + * propagated to listeners. + * + * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that + * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be + * passed as the `props` parameter to `tick()`. For example: + * + * Ticker.on("tick", handleTick); + * function handleTick(evtObj) { + * // clone the event object from Ticker, and add some custom data to it: + * var evt = evtObj.clone().set({greeting:"hello", name:"world"}); + * + * // pass it to stage.update(): + * myStage.update(evt); // subsequently calls tick() with the same param + * } + * + * // ... + * myDisplayObject.on("tick", handleDisplayObjectTick); + * function handleDisplayObjectTick(evt) { + * console.log(evt.delta); // the delta property from the Ticker tick event object + * console.log(evt.greeting, evt.name); // custom data: "hello world" + * } + * + * @method tick + * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property. + **/ + p.tick = function(props) { + if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; } + var evtObj = new createjs.Event("tick"); + if (props) { + for (var n in props) { + if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; } + } + } + this._tick(evtObj); + this.dispatchEvent("tickend"); + }; + + /** + * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}} + * event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}} + * directly, using: + * + * Ticker.addEventListener("tick", myStage); + * + * Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to + * display object tick handlers, instead of delta and paused parameters. + * @property handleEvent + * @type Function + **/ + p.handleEvent = function(evt) { + if (evt.type == "tick") { this.update(evt); } + }; + + /** + * Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`. + * @method clear + **/ + p.clear = function() { + if (!this.canvas) { return; } + var ctx = this.canvas.getContext("2d"); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); + }; + + /** + * Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can + * be specified as the src value of an image element. + * @method toDataURL + * @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color + * value is allowed. The default value is a transparent background. + * @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type + * is passed in, or if the browser does not support the specified MIME type, the default value will be used. + * @return {String} a Base64 encoded image. + **/ + p.toDataURL = function(backgroundColor, mimeType) { + var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height; + + if (backgroundColor) { + data = ctx.getImageData(0, 0, w, h); + var compositeOperation = ctx.globalCompositeOperation; + ctx.globalCompositeOperation = "destination-over"; + + ctx.fillStyle = backgroundColor; + ctx.fillRect(0, 0, w, h); + } + + var dataURL = this.canvas.toDataURL(mimeType||"image/png"); + + if(backgroundColor) { + ctx.putImageData(data, 0, 0); + ctx.globalCompositeOperation = compositeOperation; + } + + return dataURL; + }; + + /** + * Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}} + * and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}} + * and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can + * be expensive to generate, so they are disabled by default. The frequency of the events can be controlled + * independently of mouse move events via the optional `frequency` parameter. + * + *

    Example

    + * + * var stage = new createjs.Stage("canvasId"); + * stage.enableMouseOver(10); // 10 updates per second + * + * @method enableMouseOver + * @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast + * mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less + * responsive, but uses less CPU. + **/ + p.enableMouseOver = function(frequency) { + if (this._mouseOverIntervalID) { + clearInterval(this._mouseOverIntervalID); + this._mouseOverIntervalID = null; + if (frequency == 0) { + this._testMouseOver(true); + } + } + if (frequency == null) { frequency = 20; } + else if (frequency <= 0) { return; } + var o = this; + this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency)); + }; + + /** + * Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good + * practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive + * events from the page. + * + * When changing the canvas property you must disable the events on the old canvas, and enable events on the + * new canvas or mouse events will not work as expected. For example: + * + * myStage.enableDOMEvents(false); + * myStage.canvas = anotherCanvas; + * myStage.enableDOMEvents(true); + * + * @method enableDOMEvents + * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true. + **/ + + p.enableDOMEvents = function(enable) { + if (enable == null) { enable = true; } + var n, o, ls = this._eventListeners; + if (!enable && ls) { + for (n in ls) { + o = ls[n]; + if(n!=="mousemove"){ + o.t.removeEventListener(n, o.f, false); + delete this._eventListeners[n]; + } + } + } else if (enable && this.canvas) { + var t = window.addEventListener ? window : document; + var _this = this; + if(this._eventListeners===undefined)ls=this._eventListeners={}; + if(ls["mousemove"]===undefined){ + ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} }; + ls["mousemove"].t.addEventListener("mousemove",ls["mousemove"].f, false); + } + if(ls["mouseup"]===undefined){ + ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} }; + ls["mouseup"].t.addEventListener("mouseup",ls["mouseup"].f, false); + } + if(ls["dblclick"]===undefined){ + ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} }; + ls["dblclick"].t.addEventListener("dblclick",ls["dblclick"].f, false); + } + if(ls["mousedown"]===undefined){ + ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} }; + ls["mousedown"].t.addEventListener("mousedown",ls["mousedown"].f, false); + } + } + }; + + /** + * Stage instances cannot be cloned. + * @method clone + **/ + p.clone = function() { + throw("Stage cannot be cloned."); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Stage (name="+ this.name +")]"; + }; + + +// private methods: + /** + * @method _getElementRect + * @protected + * @param {HTMLElement} e + **/ + p._getElementRect = function(e) { + var bounds; + try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9 + catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; } + + var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0); + var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0); + + var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility. + var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth); + var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth); + var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth); + var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth); + + // note: in some browsers bounds properties are read only. + return { + left: bounds.left+offX+padL, + right: bounds.right+offX-padR, + top: bounds.top+offY+padT, + bottom: bounds.bottom+offY-padB + } + }; + + /** + * @method _getPointerData + * @protected + * @param {Number} id + **/ + p._getPointerData = function(id) { + var data = this._pointerData[id]; + if (!data) { data = this._pointerData[id] = {x:0,y:0}; } + return data; + }; + + /** + * @method _handleMouseMove + * @protected + * @param {MouseEvent} e + **/ + p._handleMouseMove = function(e) { + if(!e){ e = window.event; } + this._handlePointerMove(-1, e, e.pageX, e.pageY); + }; + + /** + * @method _handlePointerMove + * @protected + * @param {Number} id + * @param {Event} e + * @param {Number} pageX + * @param {Number} pageY + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + **/ + p._handlePointerMove = function(id, e, pageX, pageY, owner) { + if (this._prevStage && owner === undefined) { return; } // redundant listener. + if (!this.canvas) { return; } + var nextStage=this._nextStage, o=this._getPointerData(id); + + var inBounds = o.inBounds; + this._updatePointerPosition(id, e, pageX, pageY); + if (inBounds || o.inBounds || this.mouseMoveOutside) { + if (id === -1 && o.inBounds == !inBounds) { + this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e); + } + + this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e); + this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e); + } + + nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null); + }; + + /** + * @method _updatePointerPosition + * @protected + * @param {Number} id + * @param {Event} e + * @param {Number} pageX + * @param {Number} pageY + **/ + p._updatePointerPosition = function(id, e, pageX, pageY) { + var rect = this._getElementRect(this.canvas); + pageX -= rect.left; + pageY -= rect.top; + + var w = this.canvas.width; + var h = this.canvas.height; + pageX /= (rect.right-rect.left)/w; + pageY /= (rect.bottom-rect.top)/h; + var o = this._getPointerData(id); + if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) { + o.x = pageX; + o.y = pageY; + } else if (this.mouseMoveOutside) { + o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX); + o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY); + } + + o.posEvtObj = e; + o.rawX = pageX; + o.rawY = pageY; + + if (id === this._primaryPointerID || id === -1) { + this.mouseX = o.x; + this.mouseY = o.y; + this.mouseInBounds = o.inBounds; + } + }; + + /** + * @method _handleMouseUp + * @protected + * @param {MouseEvent} e + **/ + p._handleMouseUp = function(e) { + this._handlePointerUp(-1, e, false); + }; + + /** + * @method _handlePointerUp + * @protected + * @param {Number} id + * @param {Event} e + * @param {Boolean} clear + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + **/ + p._handlePointerUp = function(id, e, clear, owner) { + var nextStage = this._nextStage, o = this._getPointerData(id); + if (this._prevStage && owner === undefined) { return; } // redundant listener. + + var target=null, oTarget = o.target; + if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); } + + if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; } + + if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); } + this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e); + + if (clear) { + if (id==this._primaryPointerID) { this._primaryPointerID = null; } + delete(this._pointerData[id]); + } else { o.target = null; } + + nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this); + }; + + /** + * @method _handleMouseDown + * @protected + * @param {MouseEvent} e + **/ + p._handleMouseDown = function(e) { + this._handlePointerDown(-1, e, e.pageX, e.pageY); + }; + + /** + * @method _handlePointerDown + * @protected + * @param {Number} id + * @param {Event} e + * @param {Number} pageX + * @param {Number} pageY + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + **/ + p._handlePointerDown = function(id, e, pageX, pageY, owner) { + if (this.preventSelection) { e.preventDefault(); } + if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over. + + if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); } + var target = null, nextStage = this._nextStage, o = this._getPointerData(id); + if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); } + + if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; } + this._dispatchMouseEvent(target, "mousedown", true, id, o, e); + + nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this); + }; + + /** + * @method _testMouseOver + * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target) + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + * @param {Stage} eventTarget The stage that the cursor is actively over. + * @protected + **/ + p._testMouseOver = function(clear, owner, eventTarget) { + if (this._prevStage && owner === undefined) { return; } // redundant listener. + + var nextStage = this._nextStage; + if (!this._mouseOverIntervalID) { + // not enabled for mouseover, but should still relay the event. + nextStage&&nextStage._testMouseOver(clear, owner, eventTarget); + return; + } + var o = this._getPointerData(-1); + // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs. + if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; } + + var e = o.posEvtObj; + var isEventTarget = eventTarget || e&&(e.target == this.canvas); + var target=null, common = -1, cursor="", t, i, l; + + if (!owner && (clear || this.mouseInBounds && isEventTarget)) { + target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true); + this._mouseOverX = this.mouseX; + this._mouseOverY = this.mouseY; + } + + var oldList = this._mouseOverTarget||[]; + var oldTarget = oldList[oldList.length-1]; + var list = this._mouseOverTarget = []; + + // generate ancestor list and check for cursor: + t = target; + while (t) { + list.unshift(t); + if (!cursor) { cursor = t.cursor; } + t = t.parent; + } + this.canvas.style.cursor = cursor; + if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; } + + // find common ancestor: + for (i=0,l=list.length; icommon; i--) { + this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target); + } + + for (i=list.length-1; i>common; i--) { + this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget); + } + + if (oldTarget != target) { + this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget); + } + + nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this); + }; + + /** + * @method _handleDoubleClick + * @protected + * @param {MouseEvent} e + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + **/ + p._handleDoubleClick = function(e, owner) { + var target=null, nextStage=this._nextStage, o=this._getPointerData(-1); + if (!owner) { + target = this._getObjectsUnderPoint(o.x, o.y, null, true); + this._dispatchMouseEvent(target, "dblclick", true, -1, o, e); + } + nextStage&&nextStage._handleDoubleClick(e, owner || target && this); + }; + + /** + * @method _dispatchMouseEvent + * @protected + * @param {DisplayObject} target + * @param {String} type + * @param {Boolean} bubbles + * @param {Number} pointerId + * @param {Object} o + * @param {MouseEvent} [nativeEvent] + * @param {DisplayObject} [relatedTarget] + **/ + p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) { + // TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC. + if (!target || (!bubbles && !target.hasEventListener(type))) { return; } + /* + // TODO: account for stage transformations? + this._mtx = this.getConcatenatedMatrix(this._mtx).invert(); + var pt = this._mtx.transformPoint(o.x, o.y); + var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY); + */ + var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget); + target.dispatchEvent(evt); + }; + + + createjs.Stage = createjs.promote(Stage, "Container"); +}()); + +//############################################################################## +// StageGL.js +//############################################################################## + +this.createjs = this.createjs||{}; + +/* + * README IF EDITING: + * 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 + * 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. + */ + +(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. + * + * 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 + * support WebGL, content will automatically be rendered to canvas 2D context instead. + * + *

    Limitations

    + * - {{#crossLink "Shape"}}{{/crossLink}}, {{#crossLink "Shadow"}}{{/crossLink}}, and {{#crossLink "Text"}}{{/crossLink}} + * are not rendered when added to the display list. + * - To display something StageGL cannot render, {{#crossLink "displayObject/cache"}}{{/crossLink}} the object. + * Caches can be rendered regardless of source. + * - Images are wrapped as a webGL "Texture". Each graphics card has a limit to its concurrent Textures, too many + * Textures will noticeably slow performance. + * - Each cache counts as an individual Texture. As such {{#crossLink "SpriteSheet"}}{{/crossLink}} and + * {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} are recommended practices to help keep texture counts low. + * - To use any image node (DOM Image/Canvas Element) between multiple StageGL instances it must be a + * {{#crossLink "Bitmap/clone"}}{{/crossLink}}, otherwise the GPU texture loading and tracking will get confused. + * - to avoid an up/down scaled render you must call {{#crossLink "StageGL/updateViewport"}}{{/crossLink}} if you + * 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. + * + *

    Example

    + * This example creates a StageGL instance, adds a child to it, then uses the EaselJS {{#crossLink "Ticker"}}{{/crossLink}} + * to update the child and redraw the stage. + * + * var stage = new createjs.StageGL("canvasElementId"); + * + * var image = new createjs.Bitmap("imagePath.png"); + * stage.addChild(image); + * + * createjs.Ticker.on("tick", handleTick); + * + * function handleTick(event) { + * image.x += 10; + * 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. + * @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. + * @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). + * @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. + */ + function StageGL(canvas, options) { + this.Stage_constructor(canvas); + + 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; + } + +// public properties: + /** + * Console log potential issues and problems. This is designed to have minimal performance impact, so + * if extensive debugging information is required, this may be inadequate. See {{#crossLink "WebGLInspector"}}{{/crossLink}} + * @property vocalDebug + * @type {Boolean} + * @default false + */ + this.vocalDebug = false; + +// private properties: + /** + * Specifies whether or not the canvas is auto-cleared by WebGL. The WebGL spec discourages `true`. + * If true, the canvas is NOT auto-cleared by WebGL. Used when the canvas context is created and requires + * context re-creation to update. + * @property _preserveBuffer + * @protected + * @type {Boolean} + * @default false + */ + this._preserveBuffer = preserveBuffer||false; + + /** + * Specifies whether or not the browser's WebGL implementation should try to perform anti-aliasing. + * @property _antialias + * @protected + * @type {Boolean} + * @default false + */ + this._antialias = antialias||false; + + /** + * Specifies whether or not the browser's WebGL implementation should be transparent. + * @property _transparent + * @protected + * @type {Boolean} + * @default false + */ + 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} + * @default null + */ + this._autoPurge = undefined; + this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating + + /** + * The width in px of the drawing surface saved in memory. + * @property _viewportWidth + * @protected + * @type {Number} + * @default 0 + */ + this._viewportWidth = 0; + + /** + * The height in px of the drawing surface saved in memory. + * @property _viewportHeight + * @protected + * @type {Number} + * @default 0 + */ + this._viewportHeight = 0; + + /** + * A 2D projection matrix used to convert WebGL's viewspace into canvas co-ordinates. Regular canvas display + * uses Top-Left values of [0,0] where WebGL uses a Center [0,0] Top-Right [1,1] (euclidean) system. + * @property _projectionMatrix + * @protected + * @type {Float32Array} + * @default null + */ + this._projectionMatrix = null; + + /** + * The current WebGL canvas context. Often shorthanded to just "gl" in many parts of the code. + * @property _webGLContext + * @protected + * @type {WebGLRenderingContext} + * @default null + */ + this._webGLContext = null; + + /** + * The color to use when the WebGL canvas has been cleared. May appear as a background color. Defaults to grey. + * @property _clearColor + * @protected + * @type {Object} + * @default {r: 0.50, g: 0.50, b: 0.50, a: 0.00} + */ + this._clearColor = {r: 0.50, g: 0.50, b: 0.50, a: 0.00}; + + /** + * 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 + * @protected + * @type {Number} + * @default StageGL.DEFAULT_MAX_BATCH_SIZE (10000) + */ + this._maxCardsPerBatch = StageGL.DEFAULT_MAX_BATCH_SIZE; //TODO: write getter/setters for this + + /** + * The shader program used to draw the current batch. + * @property _activeShader + * @protected + * @type {WebGLProgram} + * @default null + */ + this._activeShader = null; + + /** + * The vertex position data for the current draw call. + * @property _vertices + * @protected + * @type {Float32Array} + * @default null + */ + this._vertices = null; + + /** + * The WebGL buffer attached to {{#crossLink "StageGL/_vertices:property"}}{{/crossLink}}. + * @property _vertexPositionBuffer + * @protected + * @type {WebGLBuffer} + * @default null + */ + this._vertexPositionBuffer = null; + + /** + * The vertex U/V data for the current draw call. + * @property _uvs + * @protected + * @type {Float32Array} + * @default null + */ + this._uvs = null; + + /** + * The WebGL buffer attached to {{#crossLink "StageGL/_uvs:property"}}{{/crossLink}}. + * @property _uvPositionBuffer + * @protected + * @type {WebGLBuffer} + * @default null + */ + this._uvPositionBuffer = null; + + /** + * The vertex indices data for the current draw call. + * @property _indices + * @protected + * @type {Float32Array} + * @default null + */ + this._indices = null; + + /** + * The WebGL buffer attached to {{#crossLink "StageGL/_indices:property"}}{{/crossLink}}. + * @property _textureIndexBuffer + * @protected + * @type {WebGLBuffer} + * @default null + */ + this._textureIndexBuffer = null; + + /** + * The vertices data for the current draw call. + * @property _alphas + * @protected + * @type {Float32Array} + * @default null + */ + this._alphas = null; + + /** + * The WebGL buffer attached to {{#crossLink "StageGL/_alphas:property"}}{{/crossLink}}. + * @property _alphaBuffer + * @protected + * @type {WebGLBuffer} + * @default null + */ + this._alphaBuffer = null; + + /** + * An index based lookup of every WebGL Texture currently in use. + * @property _drawTexture + * @protected + * @type {Array} + */ + this._textureDictionary = []; + + /** + * A string based lookup hash of which index a texture is stored at in the dictionary. The lookup string is + * often the src url. + * @property _textureIDs + * @protected + * @type {Object} + */ + this._textureIDs = {}; + + /** + * An array of all the textures currently loaded into the GPU. The index in the array matches the GPU index. + * @property _batchTextures + * @protected + * @type {Array} + */ + this._batchTextures = []; + + /** + * An array of all the simple filler textures used to prevent issues with missing textures in a batch. + * @property _baseTextures + * @protected + * @type {Array} + */ + 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 + * @protected + * @type {Number} + * @default 8 + */ + this._batchTextureCount = 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 + * @protected + * @type {Number} + * @default -1 + */ + this._lastTextureInsert = -1; + + /** + * The current batch being drawn, A batch consists of a call to `drawElements` on the GPU. Many of these calls + * can occur per draw. + * @property _batchId + * @protected + * @type {Number} + * @default 0 + */ + this._batchID = 0; + + /** + * The current draw being performed, may contain multiple batches. Comparing to {{#crossLink "StageGL/_batchID:property"}}{{/crossLink}} + * can reveal batching efficiency. + * @property _drawID + * @protected + * @type {Number} + * @default 0 + */ + this._drawID = 0; + + /** + * Used to prevent textures in certain GPU slots from being replaced by an insert. + * @property _slotBlackList + * @protected + * @type {Array} + */ + 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 + * @protected + * @type {Number} + * @default 0 + */ + this._lastTrackedCanvas = 0; + + /** + * 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. + * @property _cacheContainer + * @protected + * @type {Container} + * @default An instance of an EaselJS Container. + */ + this._cacheContainer = new createjs.Container(); + + // and begin + this._initializeWebGL(); + } + 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 + * the ability to get info back for a specific frame, or only calculate that one frame. + * + * //generate UV rects for all entries + * StageGL.buildUVRects( spriteSheetA ); + * //generate all, fetch the first + * var firstFrame = StageGL.buildUVRects( spriteSheetB, 0 ); + * //generate the rect for just a single frame for performance's sake + * var newFrame = StageGL.buildUVRects( dynamicSpriteSheet, newFrameIndex, true ); + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method buildUVRects + * @param {SpriteSheet} spritesheet The spritesheet to find the frames on + * @param {int} [target=-1] The index of the frame to return + * @param {Boolean} [onlyTarget=false] Whether "target" is the only frame that gets calculated + * @static + * @return {Object} the target frame if supplied and present or a generic frame {t, l, b, r} + */ + StageGL.buildUVRects = function (spritesheet, target, onlyTarget) { + if (!spritesheet || !spritesheet._frames) { return null; } + 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); + 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 + * @static + * @final + * @type {String} + * @readonly + */ + StageGL.COVER_VARYING_HEADER = ( + "precision mediump float;" + + + "varying highp vec2 vRenderCoord;" + + "varying highp vec2 vTextureCoord;" + ); + + /** + * Actual full header for the vertex shader. Includes the varying header. 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_HEADER + * @static + * @final + * @type {String} + * @readonly + */ + StageGL.COVER_VERTEX_HEADER = ( + StageGL.COVER_VARYING_HEADER + + "attribute vec2 vertexPosition;" + + "attribute vec2 uvPosition;" + + "uniform float uUpright;" + ); + + /** + * Actual full header for the fragment shader. Includes the varying header. 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_HEADER + * @static + * @final + * @type {String} + * @readonly + */ + StageGL.COVER_FRAGMENT_HEADER = ( + StageGL.COVER_VARYING_HEADER + + "uniform sampler2D uSampler;" + ); + + /** + * 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 + * @static + * @final + * @type {String} + * @readonly + */ + 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));" + + "}" + ); + + /** + * 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 + * @static + * @final + * @type {String} + * @readonly + */ + StageGL.COVER_FRAGMENT_BODY = ( + "void main(void) {" + + "vec4 color = texture2D(uSampler, vRenderCoord);" + + "gl_FragColor = color;" + + "}" + ); + +// events: + /** + * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. You can call + * {{#crossLink "Event/preventDefault"}}{{/crossLink}} on the event to cancel the draw. + * @event drawstart + */ + + /** + * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored. + * @event drawend + */ + +// getter / setters: + p._get_isWebGL = function () { + return !!this._webGLContext; + }; + + p._set_autoPurge = function (value) { + value = isNaN(value)?1200:value; + if (value != -1) { + value = value<10?10:value; + } + this._autoPurge = value; + }; + p._get_autoPurge = function () { + return Number(this._autoPurge); + }; + + try { + Object.defineProperties(p, { + /** + * Indicates whether WebGL is being used for rendering. For example, this would be `false` if WebGL is not + * supported in the browser. + * @property isWebGL + * @type {Boolean} + * @readonly + */ + isWebGL: { get: p._get_isWebGL }, + + /** + * 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. + * @property autoPurge + * @protected + * @type {Integer} + * @default 1000 + */ + autoPurge: { get: p._get_autoPurge, set: p._set_autoPurge } + }); + } catch (e) {} // TODO: use Log + + +// constructor methods: + /** + * Create and properly initialize the WebGL instance. + * @method _initializeWebGL + * @protected + * @return {WebGLRenderingContext} + */ + p._initializeWebGL = function () { + if (this.canvas) { + if (!this._webGLContext || this._webGLContext.canvas !== this.canvas) { + // A context hasn't been defined yet, + // OR the defined context belongs to a different canvas, so reinitialize. + + // 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, + 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.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); + + 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); + } + } else { + this._webGLContext = null; + } + return this._webGLContext; + }; + +// public methods: + /** + * Docced in superclass + */ + p.update = function (props) { + 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); + } + } else { + // Use 2D. + var ctx = this.canvas.getContext("2d"); + ctx.save(); + this.updateContext(ctx); + this.draw(ctx, false); + ctx.restore(); + } + this.dispatchEvent("drawend"); + }; + + /** + * Docced in superclass + */ + 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. + this.Stage_clear(); + } + }; + + /** + * Draws the stage into the supplied context if possible. Many WebGL properties only exist on their context. As such + * you cannot share contexts among many StageGLs and each context requires a unique StageGL instance. Contexts that + * don't match the context managed by this StageGL will be treated as a 2D context. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method draw + * @param {CanvasRenderingContext2D | WebGLRenderingContext} context The 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). + * @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 { + return this.Stage_draw(context, ignoreCache); + } + }; + + /** + * Draws the target into the correct context, be it a canvas or Render Texture using WebGL. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @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 { + 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; + }; + + /** + * 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; + } + if (!result) { + throw "Problems creating render textures, known causes include using too much VRAM by not releasing WebGL texture instances"; + } + target.__lastRT = result; + return result; + }; + + /** + * For every image encountered StageGL registers and tracks it automatically. This tracking can cause memory leaks + * if not purged. StageGL, by default, automatically purges them. This does have a cost and may unfortunately find + * false positives. This function is for manual management of this memory instead of the automatic system controlled + * by the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property. + * + * This function will recursively remove all textures found on the object, its children, cache, etc. It will uncache + * objects and remove any texture it finds REGARDLESS of whether it is currently in use elsewhere. It is up to the + * 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. + * @method releaseTexture + * @param {DisplayObject | Texture | Image | Canvas} item An object that used the texture to be discarded. + */ + p.releaseTexture = function (item) { + var i, l; + if (!item) { return; } + + // this is a container object + if (item.children) { + for (i = 0, l = item.children.length; i < l; i++) { + this.releaseTexture(item.children[i]); + } + } + + // this has a cache canvas + if (item.cacheCanvas) { + item.uncache(); + } + + var foundImage = undefined; + if (item._storeID !== undefined) { + // this is a texture itself + if (item === this._textureDictionary[item._storeID]) { + this._killTextureObject(item); + item._storeID = undefined; + return; + } + + // this is an image or canvas + foundImage = item; + } else if (item._webGLRenderStyle === 2) { + // this is a Bitmap class + foundImage = item.image; + } 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]); + } + return; + } + + // did we find anything + if (foundImage === undefined) { + if (this.vocalDebug) { + console.log("No associated texture found on release"); + } + return; + } + + // remove it + this._killTextureObject(this._textureDictionary[foundImage._storeID]); + foundImage._storeID = undefined; + }; + + /** + * Similar to {{#crossLink "releaseTexture"}}{{/crossLink}}, but this function differs by searching for textures to + * release. It works by assuming that it can purge any texture which was last used more than "count" draw calls ago. + * Because this process is unaware of the objects and whether they may be used on your stage, false positives can + * occur. It is recommended to manually manage your memory with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}}, + * however, there are many use cases where this is simpler and error-free. This process is also run by default under + * the hood to prevent leaks. To disable it see the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property. + * @method purgeTextures + * @param {Number} [count=100] How many renders ago the texture was last used + */ + p.purgeTextures = function (count) { + if (count == undefined){ count = 100; } + + var dict = this._textureDictionary; + var l = dict.length; + for (var i= 0; inot 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. + */ + p.updateViewport = function (width, height) { + this._viewportWidth = width|0; + this._viewportHeight = height|0; + var gl = this._webGLContext; + + if (gl) { + gl.viewport(0, 0, this._viewportWidth, this._viewportHeight); + + // WebGL works with a -1,1 space on its screen. It also follows Y-Up + // we need to flip the y, scale and then translate the co-ordinates to match this + // 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, 0, 1, 0, + -1, 1, 0.1, 0 + ]); + // 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; + } + }; + + /** + * Fetches the shader compiled and set up to work with the provided filter/object. The shader is compiled on first + * use and returned on subsequent calls. + * @method getFilterShader + * @param {Filter|Object} filter The object which will provide the information needed to construct the filter shader. + * @return {WebGLProgram} + */ + p.getFilterShader = function (filter) { + if (!filter) { filter = this; } + + var gl = this._webGLContext; + var targetShader = this._activeShader; + + if (filter._builtShader) { + targetShader = filter._builtShader; + if (filter.shaderParamSetup) { + gl.useProgram(targetShader); + filter.shaderParamSetup(gl, this, targetShader); + } + } else { + try { + targetShader = this._fetchShaderProgram( + gl, "filter", + filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY, + filter.shaderParamSetup && filter.shaderParamSetup.bind(filter) + ); + filter._builtShader = targetShader; + targetShader._name = filter.toString(); + } catch (e) { + console && console.log("SHADER SWITCH FAILURE", e); + } + } + return targetShader; + }; + + /** + * Returns a base texture that has no image or data loaded. Not intended for loading images. It may return `null` + * in some error cases, and trying to use a "null" texture can cause renders to fail. + * @method getBaseTexture + * @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 + */ + p.getBaseTexture = function (w, h) { + var width = Math.ceil(w > 0 ? w : 1) || 1; + var height = Math.ceil(h > 0 ? h : 1) || 1; + + var gl = this._webGLContext; + var texture = gl.createTexture(); + this.resizeTexture(texture, width, height); + this.setTextureParams(gl, false); + + return texture; + }; + + /** + * Resizes a supplied texture element. May generate invalid textures in some error cases such as; when the texture + * is too large, when an out of texture memory error occurs, or other error scenarios. Trying to use an invalid texture + * can cause renders to hard stop on some devices. Check the WebGL bound texture after running this function. + * + * NOTE: The supplied texture must have been made with the WebGL "texImage2D" function, all default APIs in StageGL + * use this, so this note only matters for library developers and plugins. + * + * @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 + */ + p.resizeTexture = function (texture, width,height) { + var gl = this._webGLContext; + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D( + gl.TEXTURE_2D, // target + 0, // level of detail + gl.RGBA, // internal format + width, height, 0, // width, height, border (only for array/null sourced textures) + gl.RGBA, // format (match internal format) + gl.UNSIGNED_BYTE, // type of texture(pixel color depth) + null // image data, we can do null because we're doing array data + ); + texture.width = width; + texture.height = height; + }; + + /** + * 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. + * @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. + */ + 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 + renderTexture.width = w; + renderTexture.height = h; + + // attach frame buffer to texture and provide cross links to look up each other + gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture, 0); + frameBuffer._renderTexture = renderTexture; + renderTexture._frameBuffer = frameBuffer; + + // these keep track of themselves simply to reduce complexity of some lookup code + renderTexture._storeID = this._textureDictionary.length; + this._textureDictionary[renderTexture._storeID] = renderTexture; + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + return renderTexture; + }; + + /** + * Common utility function used to apply the correct texture processing parameters for the bound texture. + * @method setTextureParams + * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. + * @param {Boolean} [isPOT=false] Marks whether the texture is "Power of Two", this may allow better quality AA. + */ + p.setTextureParams = function (gl, isPOT) { + if (isPOT && this._antialias) { + //non POT linear works in some devices, but performance is NOT good, investigate + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + } else { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + }; + + /** + * Changes the webGL clear, aka "background" color to the provided value. A transparent clear is recommended, as + * non-transparent colours may create undesired boxes around some visuals. + * + * The clear color will also be used for filters and other "render textures". The stage background will ignore the + * transparency value and display a solid color normally. For the stage to recognize and use transparency it must be + * created with the transparent flag set to `true` (see {{#crossLink "StageGL/constructor"}}{{/crossLink}})). + * + * Using "transparent white" to demonstrate, the valid data formats are as follows: + *
      + *
    • "#FFF"
    • + *
    • "#FFFFFF"
    • + *
    • "#FFFFFF00"
    • + *
    • "rgba(255,255,255,0.0)"
    • + *
    + * @method setClearColor + * @param {String|int} [color=0x00000000] The new color to use as the background + */ + p.setClearColor = function (color) { + var r, g, b, a, output; + + 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) { + output = color.slice(5, -1).split(","); + r = Number(output[0])/255; + g = Number(output[1])/255; + b = Number(output[2])/255; + a = Number(output[3]); + } + } else { // >>> is an unsigned shift which is what we want as 0x80000000 and up are negative values + r = ((color & 0xFF000000) >>> 24)/255; + g = ((color & 0x00FF0000) >>> 16)/255; + b = ((color & 0x0000FF00) >>> 8)/255; + a = (color & 0x000000FF)/255; + } + + this._clearColor.r = r || 0; + 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); + }; + + /** + * docced in subclass + */ + p.toString = function () { + return "[StageGL (name="+ this.name +")]"; + }; + +// private methods: + /** + * 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. + * @param {Canvas} canvas The DOM canvas element to attach to + * @param {Object} options The options to be handed into the WebGL object, see WebGL spec + * @method _fetchWebGLContext + * @protected + * @return {WebGLRenderingContext} The WebGL context, may return undefined in error scenarios + */ + p._fetchWebGLContext = function (canvas, options) { + var gl; + + try { + gl = canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options); + } catch (e) { + // don't do anything in catch, null check will handle it + } + + if (!gl) { + var msg = "Could not initialize WebGL"; + console.error?console.error(msg):console.log(msg); + } else { + gl.viewportWidth = canvas.width; + gl.viewportHeight = canvas.height; + } + + return gl; + }; + + /** + * Create the completed Shader Program from the vertex and fragment shaders. Allows building of custom shaders for + * 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. + * 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 + * {{#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 + * {{#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. + * @protected + * @return {WebGLProgram} The compiled and linked shader + */ + p._fetchShaderProgram = function (gl, shaderName, customVTX, customFRAG, shaderParamSetup) { + 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; + } + + // create the separate vars + var vertexShader = this._createShader(gl, gl.VERTEX_SHADER, targetVtx); + var fragmentShader = this._createShader(gl, gl.FRAGMENT_SHADER, targetFrag); + + // link them together + var shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + shaderProgram._type = shaderName; + + // check compile status + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + gl.useProgram(this._activeShader); + throw gl.getProgramInfoLog(shaderProgram); + } + + // 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); + + shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); + gl.uniform1i(shaderProgram.samplerUniform, 0); + + shaderProgram.uprightUniform = gl.getUniformLocation(shaderProgram, "uUpright"); + gl.uniform1f(shaderProgram.uprightUniform, 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; + } + + shaderProgram.samplerData = samplers; + shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); + gl.uniform1iv(shaderProgram.samplerUniform, samplers); + + shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix"); + break; + } + + gl.useProgram(this._activeShader); + return shaderProgram; + }; + + /** + * Creates a shader from the specified string replacing templates. Template items are defined via `{{` `key` `}}``. + * @method _createShader + * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. + * @param {Number} type The type of shader to create. gl.VERTEX_SHADER | gl.FRAGMENT_SHADER + * @param {String} str The definition for the shader. + * @return {WebGLShader} + * @protected + */ + p._createShader = function (gl, type, str) { + // inject the static number + str = str.replace(/{{count}}/g, 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 gl.MAX_TEXTURE_SIZE || image.height > gl.MAX_TEXTURE_SIZE){ + console && console.error("Oversized Texture: "+ image.width+"x"+image.height +" vs "+ gl.MAX_TEXTURE_SIZE +"max"); + } + } + }; + + /** + * Adds the texture to a spot in the current batch, forcing a draw if no spots are free. + * @method _insertTextureInBatch + * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. + * @param {WebGLTexture} texture The texture to be inserted. + * @protected + */ + p._insertTextureInBatch = function (gl, texture) { + // if it wasn't used last batch + if (this._batchTextures[texture._activeIndex] !== texture) { + // we've got to find it a a spot. + var found = -1; + var start = (this._lastTextureInsert+1) % this._batchTextureCount; + var look = start; + do { + if (this._batchTextures[look]._batchID != this._batchID && !this._slotBlacklist[look]) { + found = look; + break; + } + look = (look+1) % this._batchTextureCount; + } while (look !== start); + + // we couldn't find anywhere for it go, meaning we're maxed out + if (found === -1) { + this.batchReason = "textureOverflow"; + this._drawBuffers(gl); // <------------------------------------------------------------------------ + this.batchCardCount = 0; + found = start; + } + + // lets put it into that spot + this._batchTextures[found] = texture; + texture._activeIndex = found; + var image = texture._imageData; + if (image && image._invalid && texture._drawID !== undefined) { + this._updateTextureImageData(gl, image); + } else { + gl.activeTexture(gl.TEXTURE0 + found); + gl.bindTexture(gl.TEXTURE_2D, texture); + this.setTextureParams(gl); + } + this._lastTextureInsert = found; + } else { + var image = texture._imageData; + if (texture._storeID != undefined && image && image._invalid) { + this._updateTextureImageData(gl, image); + } + } + + texture._drawID = this._drawID; + texture._batchID = this._batchID; + }; + + /** + * Remove and clean the texture, expects a texture and is inflexible. Mostly for internal use, recommended to call + * {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} instead as it will call this with the correct texture object(s). + * Note: Testing shows this may not happen immediately, have to wait frames for WebGL to have actually adjust memory. + * @method _killTextureObject + * @param {Texture} tex The texture to be cleaned out + * @protected + */ + p._killTextureObject = function (tex) { + if (!tex) { return; } + var gl = this._webGLContext; + + // remove linkage + if (tex._storeID !== undefined && tex._storeID >= 0) { + this._textureDictionary[tex._storeID] = undefined; + for (var n in this._textureIDs) { + if (this._textureIDs[n] == tex._storeID) { delete this._textureIDs[n]; } + } + if(tex._imageData) { tex._imageData._storeID = undefined; } + tex._imageData = tex._storeID = undefined; + } + + // make sure to drop it out of an active slot + if (tex._activeIndex !== undefined && this._batchTextures[tex._activeIndex] === tex) { + this._batchTextures[tex._activeIndex] = this._baseTextures[tex._activeIndex]; + } + + // remove buffers if present + try { + if (tex._frameBuffer) { gl.deleteFramebuffer(tex._frameBuffer); } + tex._frameBuffer = undefined; + } catch(e) { + /* suppress delete errors because it's already gone or didn't need deleting probably */ + if (this.vocalDebug) { console.log(e); } + } + + // remove entry + try { + gl.deleteTexture(tex); + } catch(e) { + /* suppress delete errors because it's already gone or didn't need deleting probably */ + if (this.vocalDebug) { console.log(e); } + } + }; + + /** + * 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. + * @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); + } + this._isDrawing++; + this._drawID++; + + this.batchCardCount = 0; + this.depth = 0; + + this._appendToBatchGroup(sceneGraph, gl, new createjs.Matrix2D(), this.alpha, ignoreCache); + + this.batchReason = "drawFinish"; + this._drawBuffers(gl); // <-------------------------------------------------------- + this._isDrawing--; + }; + + /** + * 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 + * @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; + + this._backupBatchTextures(false); + + 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); + } + } + + this._backupBatchTextures(true); + + this.protectTextureSlot(lastTextureSlot, false); + this._activeShader = shaderBackup; + this._slotBlacklist = blackListBackup; + }; + + /** + * 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 + */ + p._drawFilters = function (target, filters, manager) { + 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); + + // 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); + 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; + } + + // 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); + + // draw result to canvas R -> C + this._activeShader = this.getFilterShader(this); + gl.clear(gl.COLOR_BUFFER_BIT); + this._drawCover(gl, flipY); + } 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; + } + }; + + /** + * 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 + * @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 + * @protected + */ + p._appendToBatchGroup = function (container, gl, concatMtx, concatAlpha, ignoreCache) { + // sort out shared properties + if (!container._glMtx) { container._glMtx = new createjs.Matrix2D(); } + var cMtx = container._glMtx; + cMtx.copy(concatMtx); + if (container.transformMatrix) { + cMtx.appendMatrix(container.transformMatrix); + } else { + cMtx.appendTransform( + container.x, container.y, + container.scaleX, container.scaleY, + container.rotation, container.skewX, container.skewY, + container.regX, container.regY + ); + } + + // sub components of figuring out the position an object holds + var subL, subT, subR, subB; + + // actually apply its data to the buffers + var l = container.children.length; + for (var i = 0; i < l; i++) { + var item = container.children[i]; + + if (!(item.visible && concatAlpha)) { continue; } + if (!item.cacheCanvas || ignoreCache) { + if (item._updateState){ + item._updateState(); + } + if (item.children) { + this._appendToBatchGroup(item, gl, cMtx, item.alpha * concatAlpha); + continue; + } + } + + // 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) { + this.batchReason = "vertexOverflow"; + this._drawBuffers(gl); // <------------------------------------------------------------ + this.batchCardCount = 0; + } + + // keep track of concatenated position + if (!item._glMtx) { item._glMtx = new createjs.Matrix2D(); } + var iMtx = item._glMtx; + iMtx.copy(cMtx); + if (item.transformMatrix) { + iMtx.appendMatrix(item.transformMatrix); + } else { + iMtx.appendTransform( + item.x, item.y, + item.scaleX, item.scaleY, + item.rotation, item.skewX, item.skewY, + item.regX, item.regY + ); + } + + var uvRect, texIndex, image, frame, texture, src; + var useCache = item.cacheCanvas && !ignoreCache; + + 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? + if (frame === null) { continue; } + image = frame.image; + } else { // MISC (DOM objects render themselves later) + continue; + } + + var uvs = this._uvs; + var vertices = this._vertices; + var texI = this._indices; + var alphas = this._alphas; + + // calculate texture + if (!image) { continue; } + if (image._storeID === undefined) { + // this texture is new to us so load it and add it to the batch + texture = this._loadTextureImage(gl, image); + this._insertTextureInBatch(gl, texture); + } else { + // fetch the texture (render textures know how to look themselves up to simplify this logic) + texture = this._textureDictionary[image._storeID]; + if (!texture){ + if (this.vocalDebug){ console.log("Texture should not be looked up while not being stored."); } + continue; + } + + // put it in the batch if needed + if (texture._batchID !== this._batchID) { + this._insertTextureInBatch(gl, texture); + } + } + texIndex = texture._activeIndex; + + if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas + if (!useCache && item.sourceRect) { + // calculate uvs + if (!item._uvRect) { item._uvRect = {}; } + src = item.sourceRect; + uvRect = item._uvRect; + uvRect.t = (src.y)/image.height; + uvRect.l = (src.x)/image.width; + uvRect.b = (src.y + src.height)/image.height; + uvRect.r = (src.x + src.width)/image.width; + + // calculate vertices + subL = 0; subT = 0; + subR = src.width+subL; subB = src.height+subT; + } else { + // calculate uvs + uvRect = StageGL.UV_RECT; + // calculate vertices + if (useCache) { + 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 + var rect = frame.rect; + + // calculate uvs + uvRect = frame.uvRect; + if (!uvRect) { + uvRect = StageGL.buildUVRects(item.spriteSheet, item.currentFrame, false); + } + + // calculate vertices + subL = -frame.regX; subT = -frame.regY; + subR = rect.width-frame.regX; subB = rect.height-frame.regY; + } + + // 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 + + //DHG: See Matrix2D.transformPoint for why this math specifically + // apply vertices + vertices[offV2] = subL *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+1] = subL *iMtx.b + subT *iMtx.d +iMtx.ty; + vertices[offV2+2] = subL *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+3] = subL *iMtx.b + subB *iMtx.d +iMtx.ty; + vertices[offV2+4] = subR *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+5] = subR *iMtx.b + subT *iMtx.d +iMtx.ty; + vertices[offV2+6] = vertices[offV2+2]; vertices[offV2+7] = vertices[offV2+3]; + vertices[offV2+8] = vertices[offV2+4]; vertices[offV2+9] = vertices[offV2+5]; + vertices[offV2+10] = subR *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+11] = subR *iMtx.b + subB *iMtx.d +iMtx.ty; + + // apply uvs + uvs[offV2] = uvRect.l; uvs[offV2+1] = uvRect.t; + uvs[offV2+2] = uvRect.l; uvs[offV2+3] = uvRect.b; + uvs[offV2+4] = uvRect.r; uvs[offV2+5] = uvRect.t; + uvs[offV2+6] = uvRect.l; uvs[offV2+7] = uvRect.b; + uvs[offV2+8] = uvRect.r; uvs[offV2+9] = uvRect.t; + uvs[offV2+10] = uvRect.r; uvs[offV2+11] = uvRect.b; + + // apply texture + texI[offV1] = texI[offV1+1] = texI[offV1+2] = texI[offV1+3] = texI[offV1+4] = texI[offV1+5] = texIndex; + + // apply alpha + alphas[offV1] = alphas[offV1+1] = alphas[offV1+2] = alphas[offV1+3] = alphas[offV1+4] = alphas[offV1+5] = item.alpha * concatAlpha; + + this.batchCardCount++; + } + }; + + /** + * 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. + * @protected + */ + p._drawBuffers = function (gl) { + if (this.batchCardCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content. + + if (this.vocalDebug) { + console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason); + } + var shaderProgram = this._activeShader; + var vertexPositionBuffer = this._vertexPositionBuffer; + var textureIndexBuffer = this._textureIndexBuffer; + var uvPositionBuffer = this._uvPositionBuffer; + var alphaBuffer = this._alphaBuffer; + + gl.useProgram(shaderProgram); + + gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._vertices); + + gl.bindBuffer(gl.ARRAY_BUFFER, textureIndexBuffer); + gl.vertexAttribPointer(shaderProgram.textureIndexAttribute, textureIndexBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._indices); + + gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer); + gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._uvs); + + gl.bindBuffer(gl.ARRAY_BUFFER, alphaBuffer); + gl.vertexAttribPointer(shaderProgram.alphaAttribute, alphaBuffer.itemSize, gl.FLOAT, false, 0, 0); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._alphas); + + gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, gl.FALSE, this._projectionMatrix); + + for (var i = 0; i < this._batchTextureCount; i++) { + var texture = this._batchTextures[i]; + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, texture); + this.setTextureParams(gl, texture.isPOT); + } + + gl.drawArrays(gl.TRIANGLES, 0, this.batchCardCount*StageGL.INDICIES_PER_CARD); + 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. + * @protected + */ + p._drawCover = function (gl, flipY) { + if (this._isDrawing > 0) { + this._drawBuffers(gl); + } + + if (this.vocalDebug) { + console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ "Cover"); + } + 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); + gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); + 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.uniform1i(shaderProgram.samplerUniform, 0); + gl.uniform1f(shaderProgram.uprightUniform, flipY?0:1); + + gl.drawArrays(gl.TRIANGLES, 0, StageGL.INDICIES_PER_CARD); + }; + + createjs.StageGL = createjs.promote(StageGL, "Stage"); +}()); + +//############################################################################## +// Bitmap.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + + /** + * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing + * HTML element, or a string. + * + *

    Example

    + * + * var bitmap = new createjs.Bitmap("imagePath.jpg"); + * + * Notes: + *
      + *
    1. When using a video source that may loop or seek, use a {{#crossLink "VideoBuffer"}}{{/crossLink}} object to + * prevent blinking / flashing. + *
    2. When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it + * will be displayed.
    3. + *
    4. Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this, + * the Bitmap can be cached.
    5. + *
    6. Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This + * happens in all browsers except recent Firefox builds.
    7. + *
    8. Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using + * methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting + * `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`
    9. + *
    + * + * @class Bitmap + * @extends DisplayObject + * @constructor + * @param {CanvasImageSource | String | Object} imageOrUri The source image to display. This can be a CanvasImageSource + * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image. + * If the latter, a new Image instance with the URL as its src will be used. + **/ + function Bitmap(imageOrUri) { + this.DisplayObject_constructor(); + + + // public properties: + /** + * The source image to display. This can be a CanvasImageSource + * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image. + * If the latter, a new Image instance with the URL as its src will be used. + * @property image + * @type CanvasImageSource | Object + **/ + if (typeof imageOrUri == "string") { + this.image = document.createElement("img"); + this.image.src = imageOrUri; + } else { + this.image = imageOrUri; + } + + /** + * Specifies an area of the source image to draw. If omitted, the whole image will be drawn. + * Notes: + *
      + *
    • that video sources must have a width / height set to work correctly with `sourceRect`
    • + *
    • Cached objects will ignore the `sourceRect` property
    • + *
    + * @property sourceRect + * @type Rectangle + * @default null + */ + this.sourceRect = null; + + // private properties: + /** + * Docced in superclass. + */ + this._webGLRenderStyle = createjs.DisplayObject._StageGL_BITMAP; + } + var p = createjs.extend(Bitmap, createjs.DisplayObject); + + +// public methods: + /** + * Constructor alias for backwards compatibility. This method will be removed in future versions. + * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. + * @method initialize + * @deprecated in favour of `createjs.promote()` + **/ + p.initialize = Bitmap; // TODO: deprecated. + + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var image = this.image; + var hasContent = this.cacheCanvas || (image && (image.naturalWidth || image.getContext || image.readyState >= 2)); + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + }; + + /** + * Draws the display object 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). + * @return {Boolean} + **/ + p.draw = function(ctx, ignoreCache) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + var img = this.image, rect = this.sourceRect; + if (img.getImage) { img = img.getImage(); } + if (!img) { return true; } + if (rect) { + // some browsers choke on out of bound values, so we'll fix them: + var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height; + if (x1 < 0) { x -= x1; x1 = 0; } + if (x2 > w) { x2 = w; } + if (y1 < 0) { y -= y1; y1 = 0; } + if (y2 > h) { y2 = h; } + ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1); + } else { + ctx.drawImage(img, 0, 0); + } + return true; + }; + + //Note, the doc sections below document using the specified APIs (from DisplayObject) from + //Bitmap. This is why they have no method implementations. + + /** + * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. + * You should not cache Bitmap instances as it can degrade performance. + * + * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. + * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} + * method. + * @method cache + **/ + + /** + * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. + * You should not cache Bitmap instances as it can degrade performance. + * + * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. + * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} + * method. + * @method updateCache + **/ + + /** + * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. + * You should not cache Bitmap instances as it can degrade performance. + * + * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. + * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} + * method. + * @method uncache + **/ + + /** + * Docced in superclass. + */ + p.getBounds = function() { + var rect = this.DisplayObject_getBounds(); + if (rect) { return rect; } + var image = this.image, o = this.sourceRect || image; + var hasContent = (image && (image.naturalWidth || image.getContext || image.readyState >= 2)); + return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null; + }; + + /** + * Returns a clone of the Bitmap instance. + * @method clone + * @param {Boolean} node Whether the underlying dom element should be cloned as well. + * @return {Bitmap} a clone of the Bitmap instance. + **/ + p.clone = function(node) { + var image = this.image; + if(image && node){ image = image.cloneNode(); } + var o = new Bitmap(image); + if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); } + this._cloneProps(o); + return o; + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Bitmap (name="+ this.name +")]"; + }; + + + createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject"); +}()); + +//############################################################################## +// Sprite.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of + * images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100 + * images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames, + * play frames as an animation, and even sequence animations together. + * + * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations. + * + *

    Example

    + * + * var instance = new createjs.Sprite(spriteSheet); + * instance.gotoAndStop("frameName"); + * + * Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called, + * only the first defined frame defined in the sprite sheet will be displayed. + * + * @class Sprite + * @extends DisplayObject + * @constructor + * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame + * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information. + * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially. + **/ + function Sprite(spriteSheet, frameOrAnimation) { + this.DisplayObject_constructor(); + + + // public properties: + /** + * The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}} + * definitions, this will advance non-sequentially. This will always be an integer value. + * @property currentFrame + * @type {Number} + * @default 0 + * @readonly + **/ + this.currentFrame = 0; + + /** + * Returns the name of the currently playing animation. + * @property currentAnimation + * @type {String} + * @final + * @readonly + **/ + this.currentAnimation = null; + + /** + * Prevents the animation from advancing each tick automatically. For example, you could create a sprite + * sheet of icons, set paused to true, and display the appropriate icon by setting currentFrame. + * @property paused + * @type {Boolean} + * @default false + **/ + this.paused = true; + + /** + * The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame + * data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information. + * @property spriteSheet + * @type {SpriteSheet} + * @readonly + **/ + this.spriteSheet = spriteSheet; + + /** + * Specifies the current frame index within the currently playing animation. When playing normally, this will increase + * from 0 to n-1, where n is the number of frames in the current animation. + * + * This could be a non-integer value if + * using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is + * not an integer. + * @property currentAnimationFrame + * @type {Number} + * @default 0 + **/ + this.currentAnimationFrame = 0; + + /** + * By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related + * SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target + * framerate. + * + * For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will + * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will + * vary slightly between frames. + * + * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being + * passed into {{#crossLink "Stage/update"}}{{/crossLink}}. + * @property framerate + * @type {Number} + * @default 0 + **/ + this.framerate = 0; + + + // private properties: + /** + * Current animation object. + * @property _animation + * @protected + * @type {Object} + * @default null + **/ + this._animation = null; + + /** + * Current frame index. + * @property _currentFrame + * @protected + * @type {Number} + * @default null + **/ + this._currentFrame = null; + + /** + * Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame + * @property _skipAdvance + * @protected + * @type {Boolean} + * @default false + **/ + this._skipAdvance = false; + + /** + * Docced in superclass. + */ + this._webGLRenderStyle = createjs.DisplayObject._StageGL_SPRITE; + + if (frameOrAnimation != null) { this.gotoAndPlay(frameOrAnimation); } + } + var p = createjs.extend(Sprite, createjs.DisplayObject); + + /** + * Constructor alias for backwards compatibility. This method will be removed in future versions. + * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. + * @method initialize + * @deprecated in favour of `createjs.promote()` + **/ + p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of Flash/Animate spritesheet export. + + +// events: + /** + * Dispatched when an animation reaches its ends. + * @event animationend + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @param {String} name The name of the animation that just ended. + * @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping. + * @since 0.6.0 + */ + + /** + * Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick, + * or calling gotoAndPlay() or gotoAndStop(). + * @event change + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + */ + + +// public methods: + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var hasContent = this.cacheCanvas || this.spriteSheet.complete; + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + }; + + /** + * Draws the display object 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 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) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + this._normalizeFrame(); + var o = this.spriteSheet.getFrame(this._currentFrame|0); + if (!o) { return false; } + var rect = o.rect; + if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); } + return true; + }; + + //Note, the doc sections below document using the specified APIs (from DisplayObject) from + //Bitmap. This is why they have no method implementations. + + /** + * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. + * You should not cache Sprite instances as it can degrade performance. + * @method cache + **/ + + /** + * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. + * You should not cache Sprite instances as it can degrade performance. + * @method updateCache + **/ + + /** + * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. + * You should not cache Sprite instances as it can degrade performance. + * @method uncache + **/ + + /** + * Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}} + * or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain + * unchanged. + * @method play + **/ + p.play = function() { + this.paused = false; + }; + + /** + * Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} + * is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}} + * will resume playback. + * @method stop + **/ + p.stop = function() { + this.paused = true; + }; + + /** + * Sets paused to false and plays the specified animation name, named frame, or frame number. + * @method gotoAndPlay + * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to + * and begin playing. + **/ + p.gotoAndPlay = function(frameOrAnimation) { + this.paused = false; + this._skipAdvance = true; + this._goto(frameOrAnimation); + }; + + /** + * Sets paused to true and seeks to the specified animation name, named frame, or frame number. + * @method gotoAndStop + * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to + * and stop. + **/ + p.gotoAndStop = function(frameOrAnimation) { + this.paused = true; + this._goto(frameOrAnimation); + }; + + /** + * Advances the playhead. This occurs automatically each tick by default. + * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite + * or its SpriteSheet. + * @method advance + */ + p.advance = function(time) { + var fps = this.framerate || this.spriteSheet.framerate; + var t = (fps && time != null) ? time/(1000/fps) : 1; + this._normalizeFrame(t); + }; + + /** + * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to + * the origin. For example, a 90 x 70 frame with regX=50 and regY=40 would return a + * rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object. + * + * Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method. + * @method getBounds + * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully + * loaded. + **/ + p.getBounds = function() { + // TODO: should this normalizeFrame? + return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle); + }; + + /** + * Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned + * instances. + * @method clone + * @return {Sprite} a clone of the Sprite instance. + **/ + p.clone = function() { + return this._cloneProps(new Sprite(this.spriteSheet)); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Sprite (name="+ this.name +")]"; + }; + +// private methods: + /** + * @method _cloneProps + * @param {Sprite} o + * @return {Sprite} o + * @protected + **/ + p._cloneProps = function(o) { + this.DisplayObject__cloneProps(o); + o.currentFrame = this.currentFrame; + o.currentAnimation = this.currentAnimation; + o.paused = this.paused; + o.currentAnimationFrame = this.currentAnimationFrame; + o.framerate = this.framerate; + + o._animation = this._animation; + o._currentFrame = this._currentFrame; + o._skipAdvance = this._skipAdvance; + return o; + }; + + /** + * Advances the currentFrame if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}} + * ticks. + * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. + * @protected + * @method _tick + **/ + p._tick = function(evtObj) { + if (!this.paused) { + if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); } + this._skipAdvance = false; + } + this.DisplayObject__tick(evtObj); + }; + + + /** + * Normalizes the current frame, advancing animations and dispatching callbacks as appropriate. + * @protected + * @method _normalizeFrame + **/ + p._normalizeFrame = function(frameDelta) { + frameDelta = frameDelta || 0; + var animation = this._animation; + var paused = this.paused; + var frame = this._currentFrame; + var l; + + if (animation) { + var speed = animation.speed || 1; + var animFrame = this.currentAnimationFrame; + l = animation.frames.length; + if (animFrame + frameDelta * speed >= l) { + var next = animation.next; + if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) { + // something changed in the event stack, so we shouldn't make any more changes here. + return; + } else if (next) { + // sequence. Automatically calls _normalizeFrame again with the remaining frames. + return this._goto(next, frameDelta - (l - animFrame) / speed); + } else { + // end. + this.paused = true; + animFrame = animation.frames.length - 1; + } + } else { + animFrame += frameDelta * speed; + } + this.currentAnimationFrame = animFrame; + this._currentFrame = animation.frames[animFrame | 0] + } else { + frame = (this._currentFrame += frameDelta); + l = this.spriteSheet.getNumFrames(); + if (frame >= l && l > 0) { + if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) { + // looped. + if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); } + } + } + } + frame = this._currentFrame | 0; + if (this.currentFrame != frame) { + this.currentFrame = frame; + this.dispatchEvent("change"); + } + }; + + /** + * Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}}, + * {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.) + * @property _dispatchAnimationEnd + * @private + * @type {Function} + **/ + p._dispatchAnimationEnd = function(animation, frame, paused, next, end) { + var name = animation ? animation.name : null; + if (this.hasEventListener("animationend")) { + var evt = new createjs.Event("animationend"); + evt.name = name; + evt.next = next; + this.dispatchEvent(evt); + } + // did the animation get changed in the event stack?: + var changed = (this._animation != animation || this._currentFrame != frame); + // if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame: + if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; } + return changed; + }; + + /** + * Moves the playhead to the specified frame number or animation. + * @method _goto + * @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to. + * @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0. + * @protected + **/ + p._goto = function(frameOrAnimation, frame) { + this.currentAnimationFrame = 0; + if (isNaN(frameOrAnimation)) { + var data = this.spriteSheet.getAnimation(frameOrAnimation); + if (data) { + this._animation = data; + this.currentAnimation = frameOrAnimation; + this._normalizeFrame(frame); + } + } else { + this.currentAnimation = this._animation = null; + this._currentFrame = frameOrAnimation; + this._normalizeFrame(); + } + }; + + + createjs.Sprite = createjs.promote(Sprite, "DisplayObject"); +}()); + +//############################################################################## +// Shape.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}} + * instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape + * instances to display the same vector graphics with different positions or transforms. + * + * If the vector art will not + * change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the + * rendering cost. + * + *

    Example

    + * + * var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100); + * var shape = new createjs.Shape(graphics); + * + * //Alternatively use can also use the graphics property of the Shape class to renderer the same as above. + * var shape = new createjs.Shape(); + * shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100); + * + * @class Shape + * @extends DisplayObject + * @constructor + * @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created. + **/ + function Shape(graphics) { + this.DisplayObject_constructor(); + + + // public properties: + /** + * The graphics instance to display. + * @property graphics + * @type Graphics + **/ + this.graphics = graphics ? graphics : new createjs.Graphics(); + } + var p = createjs.extend(Shape, createjs.DisplayObject); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. + + +// public methods: + /** + * Returns true or false indicating whether the Shape would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty()); + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + }; + + /** + * Draws the Shape 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). + * @return {Boolean} + **/ + p.draw = function(ctx, ignoreCache) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + this.graphics.draw(ctx, this); + return true; + }; + + /** + * Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to + * their defaults (for example .parent). + * @method clone + * @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be + * cloned. If false, the Graphics instance will be shared with the new Shape. + **/ + p.clone = function(recursive) { + var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics; + return this._cloneProps(new Shape(g)); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Shape (name="+ this.name +")]"; + }; + + + createjs.Shape = createjs.promote(Shape, "DisplayObject"); +}()); + +//############################################################################## +// Text.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the + * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML + * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} + * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}. + * + * Please note that Text does not support HTML text, and can only display one font style at a time. To use + * multiple font styles, you will need to create multiple text instances, and position them manually. + * + *

    Example

    + * + * var text = new createjs.Text("Hello World", "20px Arial", "#ff7700"); + * text.x = 100; + * text.textBaseline = "alphabetic"; + * + * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser + * before it can be displayed. + * + * Note: Text can be expensive to generate, so cache instances where possible. Be aware that not all + * browsers will render Text exactly the same. + * @class Text + * @extends DisplayObject + * @constructor + * @param {String} [text] The text to display. + * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold + * 36px Arial"). + * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. + * "#F00", "red", or "#FF0000"). + **/ + function Text(text, font, color) { + this.DisplayObject_constructor(); + + + // public properties: + /** + * The text to display. + * @property text + * @type String + **/ + this.text = text; + + /** + * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial"). + * @property font + * @type String + **/ + this.font = font; + + /** + * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000". + * It will also accept valid canvas fillStyle values. + * @property color + * @type String + **/ + this.color = color; + + /** + * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed + * information view the + * + * whatwg spec. Default is "left". + * @property textAlign + * @type String + **/ + this.textAlign = "left"; + + /** + * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or + * "bottom". For detailed information view the + * whatwg spec. Default is "top". + * @property textBaseline + * @type String + */ + this.textBaseline = "top"; + + /** + * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or + * shrunk to make it fit in this width. For detailed information view the + * + * whatwg spec. + * @property maxWidth + * @type Number + */ + this.maxWidth = null; + + /** + * If greater than 0, the text will be drawn as a stroke (outline) of the specified width. + * @property outline + * @type Number + **/ + this.outline = 0; + + /** + * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0, + * the value of getMeasuredLineHeight is used. + * @property lineHeight + * @type Number + **/ + this.lineHeight = 0; + + /** + * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null, + * the text will not be wrapped. + * @property lineWidth + * @type Number + **/ + this.lineWidth = null; + } + var p = createjs.extend(Text, createjs.DisplayObject); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. + + +// static properties: + /** + * @property _workingContext + * @type CanvasRenderingContext2D + * @private + **/ + var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); + if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; } + + +// constants: + /** + * Lookup table for the ratio to offset bounds x calculations based on the textAlign property. + * @property H_OFFSETS + * @type Object + * @protected + * @static + **/ + Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1}; + + /** + * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property. + * @property H_OFFSETS + * @type Object + * @protected + * @static + **/ + Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1}; + + +// public methods: + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var hasContent = this.cacheCanvas || (this.text != null && this.text !== ""); + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + }; + + /** + * Draws the Text 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 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) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + + var col = this.color || "#000"; + if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; } + else { ctx.fillStyle = col; } + + this._drawText(this._prepContext(ctx)); + return true; + }; + + /** + * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value. + * @method getMeasuredWidth + * @return {Number} The measured, untransformed width of the text. + **/ + p.getMeasuredWidth = function() { + return this._getMeasuredWidth(this.text); + }; + + /** + * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured + * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts. + * @method getMeasuredLineHeight + * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is + * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts. + **/ + p.getMeasuredLineHeight = function() { + return this._getMeasuredWidth("M")*1.2; + }; + + /** + * Returns the approximate height of multi-line text by multiplying the number of lines against either the + * lineHeight (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that + * this operation requires the text flowing logic to run, which has an associated CPU cost. + * @method getMeasuredHeight + * @return {Number} The approximate height of the untransformed multi-line text. + **/ + p.getMeasuredHeight = function() { + return this._drawText(null,{}).height; + }; + + /** + * Docced in superclass. + */ + p.getBounds = function() { + var rect = this.DisplayObject_getBounds(); + if (rect) { return rect; } + if (this.text == null || this.text === "") { return null; } + var o = this._drawText(null, {}); + var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width; + var x = w * Text.H_OFFSETS[this.textAlign||"left"]; + var lineHeight = this.lineHeight||this.getMeasuredLineHeight(); + var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"]; + return this._rectangle.setValues(x, y, w, o.height); + }; + + /** + * Returns an object with width, height, and lines properties. The width and height are the visual width and height + * of the drawn text. The lines property contains an array of strings, one for + * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing + * whitespace removed. + * @method getMetrics + * @return {Object} An object with width, height, and lines properties. + **/ + p.getMetrics = function() { + var o = {lines:[]}; + o.lineHeight = this.lineHeight || this.getMeasuredLineHeight(); + o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"]; + return this._drawText(null, o, o.lines); + }; + + /** + * Returns a clone of the Text instance. + * @method clone + * @return {Text} a clone of the Text instance. + **/ + p.clone = function() { + return this._cloneProps(new Text(this.text, this.font, this.color)); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[Text (text="+ (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]"; + }; + + +// private methods: + /** + * @method _cloneProps + * @param {Text} o + * @protected + * @return {Text} o + **/ + p._cloneProps = function(o) { + this.DisplayObject__cloneProps(o); + o.textAlign = this.textAlign; + o.textBaseline = this.textBaseline; + o.maxWidth = this.maxWidth; + o.outline = this.outline; + o.lineHeight = this.lineHeight; + o.lineWidth = this.lineWidth; + return o; + }; + + /** + * @method _getWorkingContext + * @param {CanvasRenderingContext2D} ctx + * @return {CanvasRenderingContext2D} + * @protected + **/ + p._prepContext = function(ctx) { + ctx.font = this.font||"10px sans-serif"; + ctx.textAlign = this.textAlign||"left"; + ctx.textBaseline = this.textBaseline||"top"; + ctx.lineJoin = "miter"; + ctx.miterLimit = 2.5; + return ctx; + }; + + /** + * Draws multiline text. + * @method _drawText + * @param {CanvasRenderingContext2D} ctx + * @param {Object} o + * @param {Array} lines + * @return {Object} + * @protected + **/ + p._drawText = function(ctx, o, lines) { + var paint = !!ctx; + if (!paint) { + ctx = Text._workingContext; + ctx.save(); + this._prepContext(ctx); + } + var lineHeight = this.lineHeight||this.getMeasuredLineHeight(); + + var maxW = 0, count = 0; + var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/); + for (var i=0, l=hardLines.length; i this.lineWidth) { + // text wrapping: + var words = str.split(/(\s)/); + str = words[0]; + w = ctx.measureText(str).width; + + for (var j=1, jl=words.length; j this.lineWidth) { + if (paint) { this._drawTextLine(ctx, str, count*lineHeight); } + if (lines) { lines.push(str); } + if (w > maxW) { maxW = w; } + str = words[j+1]; + w = ctx.measureText(str).width; + count++; + } else { + str += words[j] + words[j+1]; + w += wordW; + } + } + } + + if (paint) { this._drawTextLine(ctx, str, count*lineHeight); } + if (lines) { lines.push(str); } + if (o && w == null) { w = ctx.measureText(str).width; } + if (w > maxW) { maxW = w; } + count++; + } + + if (o) { + o.width = maxW; + o.height = count*lineHeight; + } + if (!paint) { ctx.restore(); } + return o; + }; + + /** + * @method _drawTextLine + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} y + * @protected + **/ + p._drawTextLine = function(ctx, text, y) { + // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead: + if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); } + else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); } + }; + + + /** + * @method _getMeasuredWidth + * @param {String} text + * @protected + **/ + p._getMeasuredWidth = function(text) { + var ctx = Text._workingContext; + ctx.save(); + var w = this._prepContext(ctx).measureText(text).width; + ctx.restore(); + return w; + }; + + + createjs.Text = createjs.promote(Text, "DisplayObject"); +}()); + +//############################################################################## +// BitmapText.js +//############################################################################## + +this.createjs = this.createjs || {}; + +(function () { + "use strict"; + + +// constructor: + /** + * Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported using new line characters, + * but automatic wrapping is not supported. See the {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}} + * property for more information on defining glyphs. + * + * Important: While BitmapText extends Container, it is not designed to be used as one. + * As such, methods like addChild and removeChild are disabled. + * + * + * @class BitmapText + * @extends DisplayObject + * @param {String} [text=""] The text to display. + * @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs. + * @constructor + **/ + function BitmapText(text, spriteSheet) { + this.Container_constructor(); + + + // public properties: + /** + * The text to display. + * @property text + * @type String + * @default "" + **/ + this.text = text||""; + + /** + * A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character + * should have a single frame animation defined in the sprite sheet named the same as + * corresponding character. For example, the following animation definition: + * + * "A": {frames: [0]} + * + * would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form + * is also acceptable: + * + * "A": 0 + * + * Note that if a character in the text is not found in the sprite sheet, it will also + * try to use the alternate case (upper or lower). + * + * See SpriteSheet for more information on defining sprite sheet data. + * @property spriteSheet + * @type SpriteSheet + * @default null + **/ + this.spriteSheet = spriteSheet; + + /** + * The height of each line of text. If 0, then it will use a line height calculated + * by checking for the height of the "1", "T", or "L" character (in that order). If + * those characters are not defined, it will use the height of the first frame of the + * sprite sheet. + * @property lineHeight + * @type Number + * @default 0 + **/ + this.lineHeight = 0; + + /** + * This spacing (in pixels) will be added after each character in the output. + * @property letterSpacing + * @type Number + * @default 0 + **/ + this.letterSpacing = 0; + + /** + * If a space character is not defined in the sprite sheet, then empty pixels equal to + * spaceWidth will be inserted instead. If 0, then it will use a value calculated + * by checking for the width of the "1", "l", "E", or "A" character (in that order). If + * those characters are not defined, it will use the width of the first frame of the + * sprite sheet. + * @property spaceWidth + * @type Number + * @default 0 + **/ + this.spaceWidth = 0; + + + // private properties: + /** + * @property _oldProps + * @type Object + * @protected + **/ + this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0}; + + /** + * Used to track the object which this class attached listeners to, helps optimize listener attachment. + * @property _oldStage + * @type Stage + * @protected + */ + this._oldStage = null; + /** + * The event listener proxy triggered drawing draw for special circumstances. + * @property _drawAction + * @type function + * @protected + */ + this._drawAction = null; + } + var p = createjs.extend(BitmapText, createjs.Container); + +// static properties: + /** + * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains + * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be + * retained, slightly increasing memory use, but reducing instantiation. + * @property maxPoolSize + * @type Number + * @static + * @default 100 + **/ + BitmapText.maxPoolSize = 100; + + /** + * Sprite object pool. + * @type {Array} + * @static + * @private + */ + BitmapText._spritePool = []; + + +// public methods: + /** + * Docced in superclass. + **/ + p.draw = function(ctx, ignoreCache) { + if (this.DisplayObject_draw(ctx, ignoreCache)) { return; } + this._updateState(); + this.Container_draw(ctx, ignoreCache); + }; + + /** + * Docced in superclass. + **/ + p.getBounds = function() { + this._updateText(); + return this.Container_getBounds(); + }; + + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text); + return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent); + }; + + p.clone = function() { + return this._cloneProps(new BitmapText(this.text, this.spriteSheet)); + }; + + /** + * Disabled in BitmapText. + * @method addChild + **/ + /** + * Disabled in BitmapText. + * @method addChildAt + **/ + /** + * Disabled in BitmapText. + * @method removeChild + **/ + /** + * Disabled in BitmapText. + * @method removeChildAt + **/ + /** + * Disabled in BitmapText. + * @method removeAllChildren + **/ + p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {}; + + +// private methods: + /** + * Docced in superclass. + **/ + p._updateState = function() { + this._updateText(); + }; + + /** + * @method _cloneProps + * @param {BitmapText} o + * @return {BitmapText} o + * @protected + **/ + p._cloneProps = function(o) { + this.Container__cloneProps(o); + o.lineHeight = this.lineHeight; + o.letterSpacing = this.letterSpacing; + o.spaceWidth = this.spaceWidth; + return o; + }; + + /** + * @method _getFrameIndex + * @param {String} character + * @param {SpriteSheet} spriteSheet + * @return {Number} + * @protected + **/ + p._getFrameIndex = function(character, spriteSheet) { + var c, o = spriteSheet.getAnimation(character); + if (!o) { + (character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null); + if (c) { o = spriteSheet.getAnimation(c); } + } + return o && o.frames[0]; + }; + + /** + * @method _getFrame + * @param {String} character + * @param {SpriteSheet} spriteSheet + * @return {Object} + * @protected + **/ + p._getFrame = function(character, spriteSheet) { + var index = this._getFrameIndex(character, spriteSheet); + return index == null ? index : spriteSheet.getFrame(index); + }; + + /** + * @method _getLineHeight + * @param {SpriteSheet} ss + * @return {Number} + * @protected + **/ + p._getLineHeight = function(ss) { + var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0); + return frame ? frame.rect.height : 1; + }; + + /** + * @method _getSpaceWidth + * @param {SpriteSheet} ss + * @return {Number} + * @protected + **/ + p._getSpaceWidth = function(ss) { + var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0); + return frame ? frame.rect.width : 1; + }; + + /** + * @method _updateText + * @protected + **/ + p._updateText = function() { + var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet; + var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite; + + for (var n in o) { + if (o[n] != this[n]) { + o[n] = this[n]; + change = true; + } + } + if (!change) { return; } + + var hasSpace = !!this._getFrame(" ", ss); + if (!hasSpace && !spaceW) { spaceW = this._getSpaceWidth(ss); } + if (!lineH) { lineH = this._getLineHeight(ss); } + + for(var i=0, l=this.text.length; i childIndex) { + // faster than removeChild. + pool.push(sprite = kids.pop()); + sprite.parent = null; + numKids--; + } + if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; } + }; + + + createjs.BitmapText = createjs.promote(BitmapText, "Container"); +}()); + +//############################################################################## +// MovieClip.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows + * you to create objects which encapsulate timeline animations, state changes, and synched actions. The MovieClip + * class has been included in the EaselJS minified file since 0.7.0. + * + * Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have + * been made to support time-based timelines in the future. + * + *

    Example

    + * This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in + * the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}. + * + * var stage = new createjs.Stage("canvas"); + * createjs.Ticker.addEventListener("tick", stage); + * + * var mc = new createjs.MovieClip({loop:-1, labels:{myLabel:20}}); + * stage.addChild(mc); + * + * var child1 = new createjs.Shape( + * new createjs.Graphics().beginFill("#999999") + * .drawCircle(30,30,30)); + * var child2 = new createjs.Shape( + * new createjs.Graphics().beginFill("#5a9cfb") + * .drawCircle(30,30,30)); + * + * mc.timeline.addTween( + * createjs.Tween.get(child1) + * .to({x:0}).to({x:60}, 50).to({x:0}, 50)); + * mc.timeline.addTween( + * createjs.Tween.get(child2) + * .to({x:60}).to({x:0}, 50).to({x:60}, 50)); + * + * mc.gotoAndPlay("start"); + * + * It is recommended to use tween.to() to animate and set properties (use no duration to have it set + * immediately), and the tween.wait() method to create delays between animations. Note that using the + * tween.set() method to affect properties will likely not provide the desired result. + * + * @class MovieClip + * @main MovieClip + * @param {Object} [props] The configuration properties to apply to this instance (ex. `{mode:MovieClip.SYNCHED}`). + * Supported props for the MovieClip are listed below. These props are set on the corresponding instance properties except where + * specified.
      + *
    • `mode`
    • + *
    • `startPosition`
    • + *
    • `frameBounds`
    • + *
    + * + * This object will also be passed into the Timeline instance associated with this MovieClip. See the documentation + * for Timeline for a list of supported props (ex. `paused`, `labels`, `loop`, `reversed`, etc.) + * @extends Container + * @constructor + **/ + function MovieClip(props) { + this.Container_constructor(); + !MovieClip.inited&&MovieClip.init(); // static init + + var mode, startPosition, loop, labels; + + // handle old params (mode, startPosition, loop, labels): + // TODO: deprecated param handling: + if (props instanceof String || arguments.length > 1) { + mode = props; + startPosition = arguments[1]; + loop = arguments[2]; + labels = arguments[3]; + if (loop == null) { loop = -1; } + props = null; + } else if (props) { + mode = props.mode; + startPosition = props.startPosition; + loop = props.loop; + labels = props.labels; + } + if (!props) { props = {labels:labels}; } + + + // public properties: + /** + * Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED). + * See each constant for a description of the behaviour. + * @property mode + * @type String + * @default null + **/ + this.mode = mode||MovieClip.INDEPENDENT; + + /** + * Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME. + * @property startPosition + * @type Number + * @default 0 + */ + this.startPosition = startPosition||0; + + /** + * Specifies how many times this MovieClip should loop. A value of -1 indicates it should loop indefinitely. A value of + * 1 would cause it to loop once (ie. play a total of twice). + * @property loop + * @type Number + * @default -1 + */ + this.loop = loop === true ? -1 : (loop || 0); + + /** + * The current frame of the movieclip. + * @property currentFrame + * @type Number + * @default 0 + * @readonly + */ + this.currentFrame = 0; + + /** + * If true, the MovieClip's position will not advance when ticked. + * @property paused + * @type Boolean + * @default false + */ + this.paused = props.paused||false; + + /** + * If true, actions in this MovieClip's tweens will be run when the playhead advances. + * @property actionsEnabled + * @type Boolean + * @default true + */ + this.actionsEnabled = true; + + /** + * If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds + * it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT. + *

    + * For example, if you had a character animation with a "body" child MovieClip instance + * with different costumes on each frame, you could set body.autoReset = false, so that + * you can manually change the frame it is on, without worrying that it will be reset + * automatically. + * @property autoReset + * @type Boolean + * @default true + */ + this.autoReset = true; + + /** + * An array of bounds for each frame in the MovieClip. This is mainly intended for tool output. + * @property frameBounds + * @type Array + * @default null + */ + this.frameBounds = this.frameBounds||props.frameBounds; // frameBounds are set on the prototype in Animate. + + /** + * By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip + * will cause it to advance based on elapsed time between ticks as appropriate to maintain the target + * framerate. + * + * For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will + * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will + * vary slightly between frames. + * + * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being + * passed into {{#crossLink "Stage/update"}}{{/crossLink}}. + * @property framerate + * @type {Number} + * @default null + **/ + this.framerate = null; + + // set up the needed props for Timeline: + props.useTicks = props.paused = true; + + /** + * The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip + * instance is initialized. Animations are created by adding TweenJS Tween + * instances to the timeline. + * + *

    Example

    + * + * var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30); + * var mc = new createjs.MovieClip(); + * mc.timeline.addTween(tween); + * + * Elements can be added and removed from the timeline by toggling an "_off" property + * using the tweenInstance.to() method. Note that using Tween.set is not recommended to + * create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for + * frame 1. You can use the "visible" property to achieve the same effect. + * + * var tween = createjs.Tween.get(target).to({_off:false}) + * .wait(1).to({_off:true}) + * .wait(1).to({_off:false}); + * + * @property timeline + * @type Timeline + * @default null + */ + this.timeline = new createjs.Timeline(props); + + + // private properties: + /** + * @property _synchOffset + * @type Number + * @default 0 + * @private + */ + this._synchOffset = 0; + + /** + * @property _rawPosition + * @type Number + * @default -1 + * @private + */ + this._rawPosition = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1. + + /** + * @property _bound_resolveState + * @type Function + * @private + */ + this._bound_resolveState = this._resolveState.bind(this); + + + /** + * The time remaining from the previous tick, only applicable when .framerate is set. + * @property _t + * @type Number + * @private + */ + this._t = 0; + + /** + * List of display objects that are actively being managed by the MovieClip. + * @property _managed + * @type Object + * @private + */ + this._managed = {}; + } + var p = createjs.extend(MovieClip, createjs.Container); + + +// constants: + /** + * The MovieClip will advance independently of its parent, even if its parent is paused. + * This is the default mode. + * @property INDEPENDENT + * @static + * @type String + * @default "independent" + * @readonly + **/ + MovieClip.INDEPENDENT = "independent"; + + /** + * The MovieClip will only display a single frame (as determined by the startPosition property). + * @property SINGLE_FRAME + * @static + * @type String + * @default "single" + * @readonly + **/ + MovieClip.SINGLE_FRAME = "single"; + + /** + * The MovieClip will be advanced only when its parent advances and will be synched to the position of + * the parent MovieClip. + * @property SYNCHED + * @static + * @type String + * @default "synched" + * @readonly + **/ + MovieClip.SYNCHED = "synched"; + + +// static properties: + MovieClip.inited = false; + + +// static methods: + MovieClip.init = function() { + if (MovieClip.inited) { return; } + // plugins introduce some overhead to Tween, so we only install this if an MC is instantiated. + MovieClipPlugin.install(); + MovieClip.inited = true; + }; + + +// getter / setters: + /** + * Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead. + * @method _getLabels + * @protected + * @return {Array} + **/ + p._getLabels = function() { + return this.timeline.getLabels(); + }; + // MovieClip.getLabels is @deprecated. Remove for 1.1+ + p.getLabels = createjs.deprecate(p._getLabels, "MovieClip.getLabels"); + + /** + * Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead. + * @method _getCurrentLabel + * @protected + * @return {String} + **/ + p._getCurrentLabel = function() { + return this.timeline.currentLabel; + }; + // MovieClip.getCurrentLabel is @deprecated. Remove for 1.1+ + p.getCurrentLabel = createjs.deprecate(p._getCurrentLabel, "MovieClip.getCurrentLabel"); + + /** + * Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead. + * @method _getDuration + * @protected + * @return {Number} + **/ + p._getDuration = function() { + return this.timeline.duration; + }; + // MovieClip.getDuration is @deprecated. Remove for 1.1+ + p.getDuration = createjs.deprecate(p._getDuration, "MovieClip.getDuration"); + + /** + * Returns an array of objects with label and position (aka frame) properties, sorted by position. + * @property labels + * @type {Array} + * @readonly + **/ + + /** + * Returns the name of the label on or immediately before the current frame. + * @property currentLabel + * @type {String} + * @readonly + **/ + + /** + * Returns the duration of this MovieClip in seconds or ticks. + * @property totalFrames + * @type {Number} + * @readonly + **/ + + /** + * Returns the duration of this MovieClip in seconds or ticks. + * @property duration + * @type {Number} + * @readonly + **/ + try { + Object.defineProperties(p, { + labels: { get: p._getLabels }, + currentLabel: { get: p._getCurrentLabel }, + totalFrames: { get: p._getDuration }, + duration: { get: p._getDuration } + // TODO: can we just proxy .currentFrame to tl.position as well? Ditto for .loop (or just remove entirely). + }); + } catch (e) {} + + +// public methods: + /** + * Constructor alias for backwards compatibility. This method will be removed in future versions. + * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. + * @method initialize + * @deprecated in favour of `createjs.promote()` + **/ + p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of Adobe Flash/Animate + + /** + * Returns true or false indicating whether the display object would be visible if drawn to a canvas. + * This does not account for whether it would be visible within the boundaries of the stage. + * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. + * @method isVisible + * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas + **/ + p.isVisible = function() { + // children are placed in draw, so we can't determine if we have content. + return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0); + }; + + /** + * Draws the display object 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 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) { + // draw to cache first: + if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } + this._updateState(); + this.Container_draw(ctx, ignoreCache); + return true; + }; + + /** + * Sets paused to false. + * @method play + **/ + p.play = function() { + this.paused = false; + }; + + /** + * Sets paused to true. + * @method stop + **/ + p.stop = function() { + this.paused = true; + }; + + /** + * Advances this movie clip to the specified position or label and sets paused to false. + * @method gotoAndPlay + * @param {String|Number} positionOrLabel The animation name or frame number to go to. + **/ + p.gotoAndPlay = function(positionOrLabel) { + this.paused = false; + this._goto(positionOrLabel); + }; + + /** + * Advances this movie clip to the specified position or label and sets paused to true. + * @method gotoAndStop + * @param {String|Number} positionOrLabel The animation or frame name to go to. + **/ + p.gotoAndStop = function(positionOrLabel) { + this.paused = true; + this._goto(positionOrLabel); + }; + + /** + * Advances the playhead. This occurs automatically each tick by default. + * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set. + * @method advance + */ + p.advance = function(time) { + var independent = MovieClip.INDEPENDENT; + if (this.mode !== independent) { return; } // update happens in draw for synched clips + + // if this MC doesn't have a framerate, hunt ancestors for one: + var o=this, fps = o.framerate; + while ((o = o.parent) && fps === null) { if (o.mode === independent) { fps = o._framerate; } } + this._framerate = fps; + + if (this.paused) { return; } + + // calculate how many frames to advance: + var t = (fps !== null && fps !== -1 && time !== null) ? time/(1000/fps) + this._t : 1; + var frames = t|0; + this._t = t-frames; // leftover time, save to add to next advance. + + while (frames--) { this._updateTimeline(this._rawPosition+1, false); } + }; + + /** + * MovieClip instances cannot be cloned. + * @method clone + **/ + p.clone = function() { + // TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex. + throw("MovieClip cannot be cloned."); + }; + + /** + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[MovieClip (name="+ this.name +")]"; + }; - /** - * Indicates whether to include this object when running mouse interactions. Setting this to `false` for children - * of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is - * clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} - * method from returning the child. - * - * Note: In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please - * check out the latest NEXT version in GitHub for an updated version with this issue resolved. The fix will be - * provided in the next release of EaselJS. - * @property mouseEnabled - * @type {Boolean} - * @default true - **/ - this.mouseEnabled = true; - /** - * If false, the tick will not run on this display object (or its children). This can provide some performance benefits. - * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates - * on some display objects (ex. Sprite & MovieClip frame advancing, and DOMElement display properties). - * @property tickEnabled - * @type Boolean - * @default true - **/ - this.tickEnabled = true; +// private methods: + /** + * Docced in superclass. + **/ + p._updateState = function() { + if (this._rawPosition === -1 || this.mode !== MovieClip.INDEPENDENT) { this._updateTimeline(-1); } + }; - /** - * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for - * debugging. - * @property name - * @type {String} - * @default null - **/ - this.name = null; + /** + * @method _tick + * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. + * function. + * @protected + **/ + p._tick = function(evtObj) { + this.advance(evtObj&&evtObj.delta); + this.Container__tick(evtObj); + }; + + /** + * @method _goto + * @param {String|Number} positionOrLabel The animation name or frame number to go to. + * @protected + **/ + p._goto = function(positionOrLabel) { + var pos = this.timeline.resolve(positionOrLabel); + if (pos == null) { return; } + this._t = 0; + this._updateTimeline(pos, true); + }; + + /** + * @method _reset + * @private + **/ + p._reset = function() { + this._rawPosition = -1; + this._t = this.currentFrame = 0; + this.paused = false; + }; + + /** + * @method _updateTimeline + * @param {Boolean} jump Indicates whether this update is due to jumping (via gotoAndXX) to a new position. + * @protected + **/ + p._updateTimeline = function(rawPosition, jump) { + var synced = this.mode !== MovieClip.INDEPENDENT, tl = this.timeline; + if (synced) { rawPosition = this.startPosition + (this.mode===MovieClip.SINGLE_FRAME?0:this._synchOffset); } + if (rawPosition < 0) { rawPosition = 0; } + if (this._rawPosition === rawPosition && !synced) { return; } + this._rawPosition = rawPosition; + + // update timeline position, ignoring actions if this is a graphic. + tl.loop = this.loop; // TODO: should we maintain this on MovieClip, or just have it on timeline? + tl.setPosition(rawPosition, synced || !this.actionsEnabled, jump, this._bound_resolveState); + }; + + /** + * Renders position 0 without running actions or updating _rawPosition. + * Primarily used by Animate CC to build out the first frame in the constructor of MC symbols. + * NOTE: not tested when run after the MC advances past the first frame. + * @method _renderFirstFrame + * @protected + **/ + p._renderFirstFrame = function() { + var tl = this.timeline, pos = tl.rawPosition; + tl.setPosition(0, true, true, this._bound_resolveState); + tl.rawPosition = pos; + }; + + /** + * Runs via a callback after timeline property updates and before actions. + * @method _resolveState + * @protected + **/ + p._resolveState = function() { + var tl = this.timeline; + this.currentFrame = tl.position; + + for (var n in this._managed) { this._managed[n] = 1; } - /** - * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that - * contains this display object, or null if it has not been added - * to one. - * @property parent - * @final - * @type {Container} - * @default null - * @readonly - **/ - this.parent = null; + var tweens = tl.tweens; + for (var i=0, l=tweens.length; i=0; i--) { + var id = kids[i].id; + if (this._managed[id] === 1) { + this.removeChildAt(i); + delete(this._managed[id]); + } + } + }; + + /** + * @method _setState + * @param {Array} state + * @param {Number} offset + * @protected + **/ + p._setState = function(state, offset) { + if (!state) { return; } + for (var i=state.length-1;i>=0;i--) { + var o = state[i]; + var target = o.t; + var props = o.p; + for (var n in props) { target[n] = props[n]; } + this._addManagedChild(target, offset); + } + }; + + /** + * Adds a child to the timeline, and sets it up as a managed child. + * @method _addManagedChild + * @param {MovieClip} child The child MovieClip to manage + * @param {Number} offset + * @private + **/ + p._addManagedChild = function(child, offset) { + if (child._off) { return; } + this.addChildAt(child,0); + + if (child instanceof MovieClip) { + child._synchOffset = offset; + // TODO: this does not precisely match Adobe Flash/Animate, which loses track of the clip if it is renamed or removed from the timeline, which causes it to reset. + // TODO: should also reset when MovieClip loops, though that will be a bit tricky to detect. + if (child.mode === MovieClip.INDEPENDENT && child.autoReset && (!this._managed[child.id])) { child._reset(); } + } + this._managed[child.id] = 2; + }; + + /** + * @method _getBounds + * @param {Matrix2D} matrix + * @param {Boolean} ignoreTransform + * @return {Rectangle} + * @protected + **/ + p._getBounds = function(matrix, ignoreTransform) { + var bounds = this.DisplayObject_getBounds(); + if (!bounds) { + if (this.frameBounds) { bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]); } + } + if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); } + return this.Container__getBounds(matrix, ignoreTransform); + }; + + + createjs.MovieClip = createjs.promote(MovieClip, "Container"); + + + +// MovieClipPlugin for TweenJS: + /** + * This plugin works with TweenJS to prevent the startPosition + * property from tweening. + * @private + * @class MovieClipPlugin + * @constructor + **/ + function MovieClipPlugin() { + throw("MovieClipPlugin cannot be instantiated.") + } + + /** + * @property priority + * @type {Number} + * @static + * @readonly + **/ + MovieClipPlugin.priority = 100; // very high priority, should run first + + /** + * @property ID + * @type {String} + * @static + * @readonly + **/ + MovieClipPlugin.ID = "MovieClip"; + + /** + * @method install + * @static + **/ + MovieClipPlugin.install = function() { + createjs.Tween._installPlugin(MovieClipPlugin); + }; + + /** + * @method init + * @param {Tween} tween + * @param {String} prop + * @param {*} value + * @static + **/ + MovieClipPlugin.init = function(tween, prop, value) { + if (prop === "startPosition" && tween.target instanceof MovieClip) { tween._addPlugin(MovieClipPlugin); } + }; + + /** + * @method step + * @param {Tween} tween + * @param {TweenStep} step + * @param {Object} props + * @static + **/ + MovieClipPlugin.step = function(tween, step, props) {}; + + /** + * @method change + * @param {Tween} tween + * @param {TweenStep} step + * @param {*} value + * @param {Number} ratio + * @param {Object} end + * @return {*} + * @static + */ + MovieClipPlugin.change = function(tween, step, prop, value, ratio, end) { + if (prop === "startPosition") { return (ratio === 1 ? step.props[prop] : step.prev.props[prop]); } + }; + +}()); + +//############################################################################## +// SpriteSheetUtils.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: + /** + * The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s. + * A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For + * example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across + * by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated. + * @class SpriteSheetUtils + * @static + **/ + function SpriteSheetUtils() { + throw "SpriteSheetUtils cannot be instantiated"; + } + + +// private static properties: + /** + * @property _workingCanvas + * @static + * @type HTMLCanvasElement | Object + * @protected + */ + /** + * @property _workingContext + * @static + * @type CanvasRenderingContext2D + * @protected + */ + var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); + if (canvas.getContext) { + SpriteSheetUtils._workingCanvas = canvas; + SpriteSheetUtils._workingContext = canvas.getContext("2d"); + canvas.width = canvas.height = 1; + } + + +// public static methods: + /** + * Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is + * to use a spritesheet frame as the source for a bitmap fill. + * + * WARNING: In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}} + * with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this + * method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}} + * property of {{#crossLink "Bitmap"}}{{/crossLink}}. + * + * The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas. + * @method extractFrame + * @static + * @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from. + * @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation + * name is specified, only the first frame of the animation will be extracted. + * @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image. + */ + SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) { + if (isNaN(frameOrAnimation)) { + frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0]; + } + var data = spriteSheet.getFrame(frameOrAnimation); + if (!data) { return null; } + var r = data.rect; + var canvas = SpriteSheetUtils._workingCanvas; + canvas.width = r.width; + canvas.height = r.height; + SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height); + var img = document.createElement("img"); + img.src = canvas.toDataURL("image/png"); + return img; + }; - /** - * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate - * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50. - * Cached object's registration points should be set based on pre-cache conditions, not cached size. - * @property regX - * @type {Number} - * @default 0 - **/ - this.regX = 0; + // SpriteSheetUtils.addFlippedFrames is @deprecated. Remove for 1.1+ + SpriteSheetUtils.addFlippedFrames = createjs.deprecate(null, "SpriteSheetUtils.addFlippedFrames"); - /** - * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around - * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50. - * Cached object's registration points should be set based on pre-cache conditions, not cached size. - * @property regY - * @type {Number} - * @default 0 - **/ - this.regY = 0; + // SpriteSheetUtils.addFlippedFrames is @deprecated. Remove for 1.1+ + SpriteSheetUtils.mergeAlpha = createjs.deprecate(null, "SpriteSheetUtils.mergeAlpha"); - /** - * The rotation in degrees for this display object. - * @property rotation - * @type {Number} - * @default 0 - **/ - this.rotation = 0; + +// private static methods: + SpriteSheetUtils._flip = function(spriteSheet, count, h, v) { + var imgs = spriteSheet._images; + var canvas = SpriteSheetUtils._workingCanvas; + var ctx = SpriteSheetUtils._workingContext; + var il = imgs.length/count; + for (var i=0;i - * whatwg spec on compositing. For a list of supported compositeOperation value, visit - * the W3C draft on Compositing and Blending. - * @property compositeOperation - * @type {String} - * @default null + * The maximum width for the images (not individual frames) in the generated SpriteSheet. It is recommended to + * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max + * dimensions, then additional images will be created as needed. + * @property maxWidth + * @type Number + * @default 2048 + */ + this.maxWidth = 2048; + + /** + * The maximum height for the images (not individual frames) in the generated SpriteSheet. It is recommended to + * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max + * dimensions, then additional images will be created as needed. + * @property maxHeight + * @type Number + * @default 2048 **/ - this.compositeOperation = null; - + this.maxHeight = 2048; + /** - * Indicates whether the display object should be drawn to a whole pixel when - * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole - * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true). - * @property snapToPixel - * @type {Boolean} - * @default true + * The SpriteSheet that was generated. This will be null before a build is completed successfully. + * @property spriteSheet + * @type SpriteSheet **/ - this.snapToPixel = true; - + this.spriteSheet = null; + /** - * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}} - * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is - * cached. - * @property filters - * @type {Array} - * @default null + * The scale to apply when drawing all frames to the SpriteSheet. This is multiplied against any scale specified + * in the addFrame call. This can be used, for example, to generate a SpriteSheet at run time that is tailored + * to the a specific device resolution (ex. tablet vs mobile). + * @property scale + * @type Number + * @default 1 **/ - this.filters = null; - + this.scale = 1; + /** - * A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation - * will be applied relative to the display object's parent coordinates (as if it were a child of the parent). - * @property mask - * @type {Shape} - * @default null - */ - this.mask = null; - + * The padding to use between frames. This is helpful to preserve antialiasing on drawn vector content. + * @property padding + * @type Number + * @default 1 + **/ + this.padding = 1; + /** - * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}. - * The hit area will have its transformation applied relative to this display object's coordinate space (as though - * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested - * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's - * ancestors (parents). - * - * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events. - * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false. - * - * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}. - * @property hitArea - * @type {DisplayObject} - * @default null + * A number from 0.01 to 0.99 that indicates what percentage of time the builder can use. This can be + * thought of as the number of seconds per second the builder will use. For example, with a timeSlice value of 0.3, + * the builder will run 20 times per second, using approximately 15ms per build (30% of available time, or 0.3s per second). + * Defaults to 0.3. + * @property timeSlice + * @type Number + * @default 0.3 + **/ + this.timeSlice = 0.3; + + /** + * A value between 0 and 1 that indicates the progress of a build, or -1 if a build has not + * been initiated. + * @property progress + * @type Number + * @default -1 + * @readonly */ - this.hitArea = null; + this.progress = -1; /** - * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display - * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to - * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants. - * @property cursor - * @type {String} - * @default null + * A {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} value that will be passed to new {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are + * created. If no framerate is specified (or it is 0), then SpriteSheets will use the {{#crossLink "Ticker"}}{{/crossLink}} + * framerate. + * @property framerate + * @type Number + * @default 0 */ - this.cursor = null; - - + this.framerate = framerate || 0; + + // private properties: /** - * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} - * @property _cacheScale + * @property _frames * @protected - * @type {Number} - * @default 1 - * @deprecated + * @type Array **/ - + this._frames = []; + /** - * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} - * @property _cacheDataURLID + * @property _animations * @protected - * @type {Number} - * @default 0 - * @deprecated - */ - + * @type Array + **/ + this._animations = {}; + /** - * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} - * @property _cacheDataURL + * @property _data * @protected - * @type {String} - * @default null - * @deprecated - */ - + * @type Array + **/ + this._data = null; + /** - * @property _props + * @property _nextFrameIndex * @protected - * @type {DisplayObject} - * @default null + * @type Number **/ - this._props = new createjs.DisplayProps(); - + this._nextFrameIndex = 0; + /** - * @property _rectangle + * @property _index * @protected - * @type {Rectangle} - * @default null + * @type Number **/ - this._rectangle = new createjs.Rectangle(); - + this._index = 0; + /** - * @property _bounds + * @property _timerID * @protected - * @type {Rectangle} - * @default null + * @type Number **/ - this._bounds = null; - + this._timerID = null; + /** - * Where StageGL should look for required display properties, matters only for leaf display objects. Containers - * or cached objects won't use this property, it's for native display of terminal elements. - * @property _webGLRenderStyle + * @property _scale * @protected - * @type {number} - * @default 0 - */ - this._webGLRenderStyle = DisplayObject._StageGL_NONE; + * @type Number + **/ + this._scale = 1; } - var p = createjs.extend(DisplayObject, createjs.EventDispatcher); - -// static properties: - /** - * Listing of mouse event names. Used in _hasMouseEventListener. - * @property _MOUSE_EVENTS - * @protected - * @static - * @type {Array} - **/ - DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"]; - - /** - * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}} - * with cross domain content. - * @property suppressCrossDomainErrors - * @static - * @type {Boolean} - * @default false - **/ - DisplayObject.suppressCrossDomainErrors = false; + var p = createjs.extend(SpriteSheetBuilder, createjs.EventDispatcher); - /** - * @property _snapToPixelEnabled - * @protected - * @static - * @type {Boolean} - * @default false - **/ - DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access. +// constants: + SpriteSheetBuilder.ERR_DIMENSIONS = "frame dimensions exceed max spritesheet dimensions"; + SpriteSheetBuilder.ERR_RUNNING = "a build is already running"; +// events: /** - * Enum like property for determining StageGL render lookup, i.e. where to expect properties. - * @property _StageGL_NONE - * @protected - * @static - * @type {number} + * Dispatched when a build completes. + * @event complete + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @since 0.6.0 */ - DisplayObject._StageGL_NONE = 0; /** - * Enum like property for determining StageGL render lookup, i.e. where to expect properties. - * @property _StageGL_SPRITE - * @protected - * @static - * @type {number} + * Dispatched when an asynchronous build has progress. + * @event progress + * @param {Object} target The object that dispatched the event. + * @param {String} type The event type. + * @param {Number} progress The current progress value (0-1). + * @since 0.6.0 */ - DisplayObject._StageGL_SPRITE = 1; - /** - * Enum like property for determining StageGL render lookup, i.e. where to expect properties. - * @property _StageGL_BITMAP - * @protected - * @static - * @type {number} - */ - DisplayObject._StageGL_BITMAP = 2; +// public methods: /** - * @property _hitTestCanvas - * @type {HTMLCanvasElement | Object} - * @static - * @protected + * Adds a frame to the {{#crossLink "SpriteSheet"}}{{/crossLink}}. Note that the frame will not be drawn until you + * call {{#crossLink "SpriteSheetBuilder/build"}}{{/crossLink}} method. The optional setup params allow you to have + * a function run immediately before the draw occurs. For example, this allows you to add a single source multiple + * times, but manipulate it or its children to change it to generate different frames. + * + * Note that the source's transformations (x, y, scale, rotate, alpha) will be ignored, except for regX/Y. To apply + * transforms to a source object and have them captured in the SpriteSheet, simply place it into a {{#crossLink "Container"}}{{/crossLink}} + * and pass in the Container as the source. + * @method addFrame + * @param {DisplayObject} source The source {{#crossLink "DisplayObject"}}{{/crossLink}} to draw as the frame. + * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the + * source to draw to the frame. If not specified, it will look for a `getBounds` method, bounds property, or + * `nominalBounds` property on the source to use. If one is not found, the frame will be skipped. + * @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1. + * @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData. + * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter. + * @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined. **/ + p.addFrame = function(source, sourceRect, scale, setupFunction, setupData) { + if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; } + var rect = sourceRect||source.bounds||source.nominalBounds; + if (!rect&&source.getBounds) { rect = source.getBounds(); } + if (!rect) { return null; } + scale = scale||1; + return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, data:setupData, index:this._frames.length, height:rect.height*scale})-1; + }; + /** - * @property _hitTestContext - * @type {CanvasRenderingContext2D} - * @static - * @protected + * Adds an animation that will be included in the created {{#crossLink "SpriteSheet"}}{{/crossLink}}. + * @method addAnimation + * @param {String} name The name for the animation. + * @param {Array} frames An array of frame indexes that comprise the animation. Ex. [3,6,5] would describe an animation + * that played frame indexes 3, 6, and 5 in that order. + * @param {String} [next] Specifies the name of the animation to continue to after this animation ends. You can + * also pass false to have the animation stop when it ends. By default it will loop to the start of the same animation. + * @param {Number} [speed] Specifies a frame advance speed for this animation. For example, a value of 0.5 would + * cause the animation to advance every second tick. Note that earlier versions used `frequency` instead, which had + * the opposite effect. **/ - var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas. - if (canvas.getContext) { - DisplayObject._hitTestCanvas = canvas; - DisplayObject._hitTestContext = canvas.getContext("2d"); - canvas.width = canvas.height = 1; - } + p.addAnimation = function(name, frames, next, speed) { + if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; } + this._animations[name] = {frames:frames, next:next, speed:speed}; + }; -// events: /** - * Dispatched when the user presses their left mouse button over the display object. See the - * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event mousedown - * @since 0.6.0 - */ + * This will take a {{#crossLink "MovieClip"}}{{/crossLink}} instance, and add its frames and labels to this + * builder. Labels will be added as an animation running from the label index to the next label. For example, if + * there is a label named "foo" at frame 0 and a label named "bar" at frame 10, in a MovieClip with 15 frames, it + * will add an animation named "foo" that runs from frame index 0 to 9, and an animation named "bar" that runs from + * frame index 10 to 14. + * + * Note that this will iterate through the full MovieClip with {{#crossLink "MovieClip/actionsEnabled:property"}}{{/crossLink}} + * set to `false`, ending on the last frame. + * @method addMovieClip + * @param {MovieClip} source The source MovieClip instance to add to the SpriteSheet. + * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to + * draw to the frame. If not specified, it will look for a {{#crossLink "DisplayObject/getBounds"}}{{/crossLink}} + * method, `frameBounds` Array, `bounds` property, or `nominalBounds` property on the source to use. If one is not + * found, the MovieClip will be skipped. + * @param {Number} [scale=1] The scale to draw the movie clip at. + * @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called + * with three parameters: the source, setupData, and the frame index. + * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter. + * @param {Function} [labelFunction] This method will be called for each MovieClip label that is added with four + * parameters: the label name, the source MovieClip instance, the starting frame index (in the movieclip timeline) + * and the end index. It must return a new name for the label/animation, or `false` to exclude the label. + **/ + p.addMovieClip = function(source, sourceRect, scale, setupFunction, setupData, labelFunction) { + if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; } + var rects = source.frameBounds; + var rect = sourceRect||source.bounds||source.nominalBounds; + if (!rect&&source.getBounds) { rect = source.getBounds(); } + if (!rect && !rects) { return; } - /** - * Dispatched when the user presses their left mouse button and then releases it while over the display object. - * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event click - * @since 0.6.0 - */ + var i, l, baseFrameIndex = this._frames.length; + var duration = source.timeline.duration; + for (i=0; i - *
  • when the mouse enters shapeA (target=shapeA)
  • - *
  • when the mouse enters shapeB (target=shapeB)
  • - * - * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters - * the aggregate myContainer content (target=myContainer). - * - * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. - * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event rollover - * @since 0.7.0 - */ + * SpriteSheetBuilder instances cannot be cloned. + * @method clone + **/ + p.clone = function() { + throw("SpriteSheetBuilder cannot be cloned."); + }; /** - * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following - * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an - * aggregate of their content. - * - * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over - * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}} - * on myContainer, two events would be received, each targeting a child element:
      - *
    1. when the mouse leaves shapeA (target=shapeA)
    2. - *
    3. when the mouse leaves shapeB (target=shapeB)
    4. - *
    - * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves - * the aggregate myContainer content (target=myContainer). - * - * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. - * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event rollout - * @since 0.7.0 - */ + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + **/ + p.toString = function() { + return "[SpriteSheetBuilder]"; + }; - /** - * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove - * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be - * useful for dragging and similar operations. - * - * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed - * (e.g. during a `pressmove` event), a `pressmove` event is still generated. However since it is no longer in the - * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive - * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressup:event"}}{{/crossLink}}` - * or `pressmove` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), - * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. - * @event pressmove - * @since 0.7.0 - */ +// private methods: /** - * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event - * will be generated on that object when that mouse press is released. This can be useful for dragging and similar - * operations. - * - * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed - * (e.g. during a `pressmove` event), a `pressup` event is still generated. However since it is no longer in the - * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive - * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressmove:event"}}{{/crossLink}}` - * or `pressup` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}), - * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`. - * @event pressup - * @since 0.7.0 - */ + * @method _startBuild + * @protected + **/ + p._startBuild = function() { + var pad = this.padding||0; + this.progress = 0; + this.spriteSheet = null; + this._index = 0; + this._scale = this.scale; + var dataFrames = []; + this._data = { + images: [], + frames: dataFrames, + framerate: this.framerate, + animations: this._animations // TODO: should we "clone" _animations in case someone adds more animations after a build? + }; + var frames = this._frames.slice(); + frames.sort(function(a,b) { return (a.height<=b.height) ? -1 : 1; }); + + if (frames[frames.length-1].height+pad*2 > this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; } + var y=0, x=0; + var img = 0; + while (frames.length) { + var o = this._fillRow(frames, y, img, dataFrames, pad); + if (o.w > x) { x = o.w; } + y += o.h; + if (!o.h || !frames.length) { + var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); + canvas.width = this._getSize(x,this.maxWidth); + canvas.height = this._getSize(y,this.maxHeight); + this._data.images[img] = canvas; + if (!o.h) { + x=y=0; + img++; + } + } + } + }; + /** - * Dispatched when the display object is added to a parent container. - * @event added - */ + * @method _setupMovieClipFrame + * @protected + * @return {Number} The width & height of the row. + **/ + p._setupMovieClipFrame = function(source, data) { + var ae = source.actionsEnabled; + source.actionsEnabled = false; + source.gotoAndStop(data.i); + source.actionsEnabled = ae; + data.f&&data.f(source, data.d, data.i); + }; /** - * Dispatched when the display object is removed from its parent container. - * @event removed - */ + * @method _getSize + * @protected + * @return {Number} The width & height of the row. + **/ + p._getSize = function(size,max) { + var pow = 4; + while (Math.pow(2,++pow) < size){} + return Math.min(max,Math.pow(2,pow)); + }; /** - * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the - * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on - * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their - * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being - * dispatched on their parent. - * @event tick - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For - * example if you called stage.update("hello"), then the params would be ["hello"]. - * @since 0.6.0 - */ - + * @method _fillRow + * @param {Array} frames + * @param {Number} y + * @param {HTMLImageElement} img + * @param {Object} dataFrames + * @param {Number} pad + * @protected + * @return {Number} The width & height of the row. + **/ + p._fillRow = function(frames, y, img, dataFrames, pad) { + var w = this.maxWidth; + var maxH = this.maxHeight; + y += pad; + var h = maxH-y; + var x = pad; + var height = 0; + for (var i=frames.length-1; i>=0; i--) { + var frame = frames[i]; + var sc = this._scale*frame.scale; + var rect = frame.sourceRect; + var source = frame.source; + var rx = Math.floor(sc*rect.x-pad); + var ry = Math.floor(sc*rect.y-pad); + var rh = Math.ceil(sc*rect.height+pad*2); + var rw = Math.ceil(sc*rect.width+pad*2); + if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; } + if (rh > h || x+rw > w) { continue; } + frame.img = img; + frame.rect = new createjs.Rectangle(x,y,rw,rh); + height = height || rh; + frames.splice(i,1); + dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)]; + x += rw; + } + return {w:x, h:height}; + }; -// getter / setters: /** - * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead. - * @method _getStage + * @method _endBuild * @protected - * @return {Stage} **/ - p._getStage = function() { - // uses dynamic access to avoid circular dependencies; - var o = this, _Stage = createjs["Stage"]; - while (o.parent) { o = o.parent; } - if (o instanceof _Stage) { return o; } - return null; + p._endBuild = function() { + this.spriteSheet = new createjs.SpriteSheet(this._data); + this._data = null; + this.progress = 1; + this.dispatchEvent("complete"); }; - // DisplayObject.getStage is @deprecated. Remove for 1.1+ - p.getStage = createjs.deprecate(p._getStage, "DisplayObject.getStage"); /** - * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one. - * @property stage - * @type {Stage} - * @readonly + * @method _run + * @protected **/ + p._run = function() { + var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50; + var t = (new Date()).getTime()+ts; + var complete = false; + while (t > (new Date()).getTime()) { + if (!this._drawNext()) { complete = true; break; } + } + if (complete) { + this._endBuild(); + } else { + var _this = this; + this._timerID = setTimeout(function() { _this._run(); }, 50-ts); + } + var p = this.progress = this._index/this._frames.length; + if (this.hasEventListener("progress")) { + var evt = new createjs.Event("progress"); + evt.progress = p; + this.dispatchEvent(evt); + } + }; /** - * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to - * determine if the cache has changed since a previous check. - * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}} - * @property cacheID - * @deprecated - * @type {Number} - * @default 0 - */ + * @method _drawNext + * @protected + * @return Boolean Returns false if this is the last draw. + **/ + p._drawNext = function() { + var frame = this._frames[this._index]; + var sc = frame.scale*this._scale; + var rect = frame.rect; + var sourceRect = frame.sourceRect; + var canvas = this._data.images[frame.img]; + var ctx = canvas.getContext("2d"); + frame.funct&&frame.funct(frame.source, frame.data); + ctx.save(); + ctx.beginPath(); + ctx.rect(rect.x, rect.y, rect.width, rect.height); + ctx.clip(); + ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc)); + ctx.scale(sc,sc); + frame.source.draw(ctx); // display object will draw itself. + ctx.restore(); + return (++this._index) < this._frames.length; + }; + + + createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher"); +}()); + +//############################################################################## +// DOMElement.js +//############################################################################## + +this.createjs = this.createjs||{}; +(function() { + "use strict"; + + +// constructor: /** - * Set both the {{#crossLink "DisplayObject/scaleX:property"}}{{/crossLink}} and the {{#crossLink "DisplayObject/scaleY"}}{{/crossLink}} - * property to the same value. Note that when you get the value, if the `scaleX` and `scaleY` are different values, - * it will return only the `scaleX`. - * @property scaleX - * @type {Number} - * @default 1 + * This class is still experimental, and more advanced use is likely to be buggy. Please report bugs. + * + * A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed + * within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is + * not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be + * drawn in front of or behind the canvas). + * + * The position of a DOMElement is relative to their parent node in the DOM. It is recommended that + * the DOM Object be added to a div that also contains the canvas so that they share the same position + * on the page. + * + * DOMElement is useful for positioning HTML elements over top of canvas content, and for elements + * that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML + * content. + * + *

    Mouse Interaction

    + * + * DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse + * events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to + * the htmlElement (note, this does not support EventDispatcher) + * + * var domElement = new createjs.DOMElement(htmlElement); + * domElement.htmlElement.onclick = function() { + * console.log("clicked"); + * } + * + * Important: This class needs to be notified it is about to be drawn, this will happen automatically + * if you call stage.update, calling stage.draw or disabling tickEnabled will miss important steps and it will render + * stale information. + * + * @class DOMElement + * @extends DisplayObject + * @constructor + * @param {HTMLElement} htmlElement A reference or id for the DOM element to manage. */ - try { - Object.defineProperties(p, { - stage: { get: p._getStage }, - cacheID: { - get: function(){ return this.bitmapCache && this.bitmapCache.cacheID }, - set: function(a){ this.bitmapCache && (this.bitmapCache.cacheID = a) } - }, - scale: { - get: function() { return this.scaleX; }, - set: function(scale) { this.scaleX = this.scaleY = scale; }, - } - }); - } catch (e) {} + function DOMElement(htmlElement) { + this.DisplayObject_constructor(); + + if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); } + this.mouseEnabled = false; + + var style = htmlElement.style; + style.position = "absolute"; + style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%"; + + + // public properties: + /** + * The DOM object to manage. + * @property htmlElement + * @type HTMLElement + */ + this.htmlElement = htmlElement; + + + // private properties: + /** + * @property _oldMtx + * @type Matrix2D + * @protected + */ + this._oldProps = null; + + /** + * Used to track the object which this class attached listeners to, helps optimize listener attachment. + * @property _oldStage + * @type Stage + * @protected + */ + this._oldStage = null; + /** + * The event listener proxy triggered drawing draw for special circumstances. + * @property _drawAction + * @type function + * @protected + */ + this._drawAction = null; + } + var p = createjs.extend(DOMElement, createjs.DisplayObject); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. // public methods: /** * Returns true or false indicating whether the display object would be visible if drawn to a canvas. * This does not account for whether it would be visible within the boundaries of the stage. - * * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. * @method isVisible * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ + */ p.isVisible = function() { - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0); + return this.htmlElement != null; }; /** * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform. - * Returns true if the draw was handled (useful for overriding functionality). - * + * 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). + * @param {Boolean} ignoreCache 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). * @return {Boolean} - **/ + */ p.draw = function(ctx, ignoreCache) { - var cache = this.bitmapCache; - if(cache && !ignoreCache) { - return cache.draw(ctx); - } - return false; + // this relies on the _tick method because draw isn't called if the parent is not visible. + // the actual update happens in _handleDrawEnd + return true; }; /** - * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow - * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}. - * @method updateContext - * @param {CanvasRenderingContext2D} ctx The canvas 2D to update. - **/ - p.updateContext = function(ctx) { - var o=this, mask=o.mask, mtx= o._props.matrix; - - if (mask && mask.graphics && !mask.graphics.isEmpty()) { - mask.getMatrix(mtx); - ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); - - mask.graphics.drawAsPath(ctx); - ctx.clip(); - - mtx.invert(); - ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); - } - - this.getMatrix(mtx); - var tx = mtx.tx, ty = mtx.ty; - if (DisplayObject._snapToPixelEnabled && o.snapToPixel) { - tx = tx + (tx < 0 ? -0.5 : 0.5) | 0; - ty = ty + (ty < 0 ? -0.5 : 0.5) | 0; - } - ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty); - ctx.globalAlpha *= o.alpha; - if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; } - if (o.shadow) { this._applyShadow(ctx, o.shadow); } + * Not applicable to DOMElement. + * @method cache + */ + p.cache = function() {}; + + /** + * Not applicable to DOMElement. + * @method uncache + */ + p.uncache = function() {}; + + /** + * Not applicable to DOMElement. + * @method updateCache + */ + p.updateCache = function() {}; + + /** + * Not applicable to DOMElement. + * @method hitTest + */ + p.hitTest = function() {}; + + /** + * Not applicable to DOMElement. + * @method localToGlobal + */ + p.localToGlobal = function() {}; + + /** + * Not applicable to DOMElement. + * @method globalToLocal + */ + p.globalToLocal = function() {}; + + /** + * Not applicable to DOMElement. + * @method localToLocal + */ + p.localToLocal = function() {}; + + /** + * DOMElement cannot be cloned. Throws an error. + * @method clone + */ + p.clone = function() { + throw("DOMElement cannot be cloned.") }; /** - * Draws the display object into a new element, which is then used for subsequent draws. Intended for complex content - * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape), - * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The - * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually - * update the cache by calling updateCache() again. You must specify the cached area via the x, y, w, - * and h parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates. - * - *

    Example

    - * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25: - * - * var shape = new createjs.Shape(); - * shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25); - * shape.cache(-25, -25, 50, 50); - * - * Note that filters need to be defined before the cache is applied or you will have to call updateCache after - * application. Check out the {{#crossLink "Filter"}}{{/crossLink}} class for more information. Some filters - * (ex. BlurFilter) may not work as expected in conjunction with the scale param. - * - * Usually, the resulting cacheCanvas will have the dimensions width * scale, height * scale, however some filters (ex. BlurFilter) - * will add padding to the canvas dimensions. - * - * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. - * This allows for easier interaction and alternate cache methods like WebGL with {{#crossLink "StageGL"}}{{/crossLink}}. - * For more information on the options object, see the BitmapCache {{#crossLink "BitmapCache/define"}}{{/crossLink}}. - * - * @method cache - * @param {Number} x The x coordinate origin for the cache region. - * @param {Number} y The y coordinate origin for the cache region. - * @param {Number} width The width of the cache region. - * @param {Number} height The height of the cache region. - * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using - * myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate - * cached elements with greater fidelity. Default is 1. - * @param {Object} [options=undefined] Specify additional parameters for the cache logic - **/ - p.cache = function(x, y, width, height, scale, options) { - if(!this.bitmapCache){ - this.bitmapCache = new createjs.BitmapCache(); + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. + */ + p.toString = function() { + return "[DOMElement (name="+ this.name +")]"; + }; + + /** + * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances + * are not full EaselJS display objects and do not participate in EaselJS mouse events. + * @event click + */ + + /** + * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances + * are not full EaselJS display objects and do not participate in EaselJS mouse events. + * @event dblClick + */ + + /** + * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances + * are not full EaselJS display objects and do not participate in EaselJS mouse events. + * @event mousedown + */ + + /** + * The HTMLElement can listen for the mouseover event, not the DOMElement instance. + * Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events. + * @event mouseover + */ + + /** + * Not applicable to DOMElement. + * @event tick + */ + + +// private methods: + /** + * @method _tick + * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. + * function. + * @protected + */ + p._tick = function(evtObj) { + var stage = this.stage; + if(stage && stage !== this._oldStage) { + this._drawAction && stage.off("drawend", this._drawAction); + this._drawAction = stage.on("drawend", this._handleDrawEnd, this); + this._oldStage = stage; + } + this.DisplayObject__tick(evtObj); + }; + + /** + * @method _handleDrawEnd + * @param {Event} evt + * @protected + */ + p._handleDrawEnd = function(evt) { + var o = this.htmlElement; + if (!o) { return; } + var style = o.style; + + var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix; + + var visibility = props.visible ? "visible" : "hidden"; + if (visibility != style.visibility) { style.visibility = visibility; } + if (!props.visible) { return; } + + var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix; + var n = 10000; // precision + + if (!oldMtx || !oldMtx.equals(mtx)) { + var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0); + style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")"; + style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)"; + if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, null); } + oldProps.matrix.copy(mtx); + } + + if (oldProps.alpha != props.alpha) { + style.opacity = ""+(props.alpha*n|0)/n; + oldProps.alpha = props.alpha; } - this.bitmapCache.define(this, x, y, width, height, scale, options); }; + + createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject"); +}()); + +//############################################################################## +// Filter.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: /** - * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error. - * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object - * will be drawn over the existing cache using the specified compositeOperation. + * 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. * *

    Example

    - * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line - * will be drawn on top of the old one. * - * // Not shown: Creating the shape, and caching it. - * shapeInstance.clear(); - * shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200); - * shapeInstance.updateCache(); + * myInstance.filters = [ + * new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0), + * new createjs.BlurFilter(5, 5, 10) + * ]; + * myInstance.cache(0,0, 100, 100); * - * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}. - * This allows for easier interaction and alternate cache methods like WebGL and {{#crossLink "StageGL"}}{{/crossLink}}. + * Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the + * 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. * - * @method updateCache - * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it. - * - * whatwg spec on compositing. + *

    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}}
    • + *
    + * + * @class Filter + * @constructor **/ - p.updateCache = function(compositeOperation) { - if(!this.bitmapCache) { - throw "cache() must be called before updateCache()"; - } - this.bitmapCache.update(compositeOperation); - }; + function Filter() { + /** + * A flag stating that this filter uses a context draw mode and cannot be batched into imageData processing. + * @property usesContext + * @type {boolean} + * @default false + */ + this.usesContext = false; + + /** + * Another filter that is required to act as part of this filter and created and managed under the hood. + * @private + * @property _multiPass + * @type {Filter} + * @default null + */ + this._multiPass = null; + + /** + * 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 + */ + this.VTX_SHADER_BODY = null; + /** + * 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 + */ + this.FRAG_SHADER_BODY = null; + } + var p = Filter.prototype; + +// public methods: /** - * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information. - * @method uncache + * Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to. + * @method getBounds + * @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned. + * @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter. **/ - p.uncache = function() { - if(this.bitmapCache) { - this.bitmapCache.release(); - this.bitmapCache = undefined; - } + p.getBounds = function(rect) { + return rect; }; /** - * Returns a data URL for the cache, or null if this display object is not cached. - * Only generated if the cache has changed, otherwise returns last result. - * @method getCacheDataURL - * @return {String} The image data url for the cache. - **/ - p.getCacheDataURL = function() { - return this.bitmapCache?this.bitmapCache.getCacheDataURL():null; - }; + * Assign any unique uniforms or other setup functionality here. + * @method shaderParamSetup + * @virtual + * @param {WebGLContext} gl The context associated with the stage performing the render. + * @param {StageGL} stage The stage instance that will be rendering. + * @param {ShaderProgram} shaderProgram The compiled shader that is going to be used to perform the render. + */ + p.shaderParamSetup = function(gl, stage, shaderProgram) {}; /** - * Transforms the specified x and y position from the coordinate space of the display object - * to the global (stage) coordinate space. For example, this could be used to position an HTML label - * over a specific point on a nested display object. Returns a Point instance with x and y properties - * correlating to the transformed coordinates on the stage. - * - *

    Example

    - * - * displayObject.x = 300; - * displayObject.y = 200; - * stage.addChild(displayObject); - * var point = displayObject.localToGlobal(100, 100); - * // Results in x=400, y=300 - * - * @method localToGlobal - * @param {Number} x The x position in the source display object to transform. - * @param {Number} y The y position in the source display object to transform. - * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. - * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates - * on the stage. + * Applies the filter to the specified context. + * @method applyFilter + * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source. + * @param {Number} x The x position to use for the source rect. + * @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. + * @return {Boolean} If the filter was applied successfully. **/ - p.localToGlobal = function(x, y, pt) { - return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point()); + p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) { + // 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); + return true; + } + return false; }; /** - * Transforms the specified x and y position from the global (stage) coordinate space to the - * coordinate space of the display object. For example, this could be used to determine - * the current mouse position within the display object. Returns a Point instance with x and y properties - * correlating to the transformed position in the display object's coordinate space. - * - *

    Example

    - * - * displayObject.x = 300; - * displayObject.y = 200; - * stage.addChild(displayObject); - * var point = displayObject.globalToLocal(100, 100); - * // Results in x=-200, y=-100 - * - * @method globalToLocal - * @param {Number} x The x position on the stage to transform. - * @param {Number} y The y position on the stage to transform. - * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. - * @return {Point} A Point instance with x and y properties correlating to the transformed position in the - * display object's coordinate space. + * Returns a string representation of this object. + * @method toString + * @return {String} a string representation of the instance. **/ - p.globalToLocal = function(x, y, pt) { - return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point()); + p.toString = function() { + return "[Filter]"; }; /** - * Transforms the specified x and y position from the coordinate space of this display object to the coordinate - * space of the target display object. Returns a Point instance with x and y properties correlating to the - * transformed position in the target's coordinate space. Effectively the same as using the following code with - * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. - * - * var pt = this.localToGlobal(x, y); - * pt = target.globalToLocal(pt.x, pt.y); - * - * @method localToLocal - * @param {Number} x The x position in the source display object to transform. - * @param {Number} y The y position on the source display object to transform. - * @param {DisplayObject} target The target display object to which the coordinates will be transformed. - * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. - * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position - * in the target's coordinate space. + * Returns a clone of this Filter instance. + * @method clone + * @return {Filter} A clone of the current Filter instance. **/ - p.localToLocal = function(x, y, target, pt) { - pt = this.localToGlobal(x, y, pt); - return target.globalToLocal(pt.x, pt.y, pt); + p.clone = function() { + return new Filter(); }; + +// private methods: + /** + * @method _applyFilter + * @param {ImageData} imageData Target ImageData instance. + * @return {Boolean} + **/ + p._applyFilter = function(imageData) { return true; }; + + + createjs.Filter = Filter; +}()); + +//############################################################################## +// BitmapCache.js +//############################################################################## +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: /** - * Shortcut method to quickly set the transform properties on the display object. All parameters are optional. - * Omitted parameters will have the default value set. - * - *

    Example

    + * The BitmapCache is an internal representation of all the cache properties and logic required in order to "cache" + * an object. This information and functionality used to be located on a {{#crossLink "DisplayObject/cache"}}{{/crossLink}} + * method in {{#crossLink "DisplayObject"}}{{/crossLink}}, but was moved to its own class. * - * displayObject.setTransform(100, 100, 2, 2); + * Caching in this context is purely visual, and will render the DisplayObject out into an image to be used instead + * of the object. The actual cache itself is still stored on the target with the {{#crossLink "DisplayObject/cacheCanvas:property"}}{{/crossLink}}. + * Working with a singular image like a {{#crossLink "Bitmap"}}{{/crossLink}} there is little benefit to performing + * a cache as it is already a single image. Caching is best done on containers containing multiple complex parts that + * do not move often, so that rendering the image instead will improve overall rendering speed. A cached object will + * not visually update until explicitly told to do so with a call to update, much like a Stage. If a cache is being + * updated every frame it is likely not improving rendering performance. Cache are best used when updates will be sparse. * - * @method setTransform - * @param {Number} [x=0] The horizontal translation (x position) in pixels - * @param {Number} [y=0] The vertical translation (y position) in pixels - * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1 - * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1 - * @param {Number} [rotation=0] The rotation, in degrees - * @param {Number} [skewX=0] The horizontal skew factor - * @param {Number} [skewY=0] The vertical skew factor - * @param {Number} [regX=0] The horizontal registration point in pixels - * @param {Number} [regY=0] The vertical registration point in pixels - * @return {DisplayObject} Returns this instance. Useful for chaining commands. - * @chainable - */ - p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { - this.x = x || 0; - this.y = y || 0; - this.scaleX = scaleX == null ? 1 : scaleX; - this.scaleY = scaleY == null ? 1 : scaleY; - this.rotation = rotation || 0; - this.skewX = skewX || 0; - this.skewY = skewY || 0; - this.regX = regX || 0; - this.regY = regY || 0; - return this; - }; + * Caching is also a co-requisite for applying filters to prevent expensive filters running constantly without need, + * and to physically enable some effects. The BitmapCache is also responsible for applying filters to objects and + * reads each {{#crossLink "Filter"}}{{/crossLink}} due to this relationship. Real-time Filters are not recommended + * performance wise when dealing with a Context2D canvas. For best performance and to still allow for some visual + * effects use a compositeOperation when possible. + * @class BitmapCache + * @constructor + **/ + function BitmapCache() { + + // public: + /** + * Width of the cache relative to the target object. + * @property width + * @protected + * @type {Number} + * @default undefined + **/ + this.width = undefined; + + /** + * Height of the cache relative to the target object. + * @property height + * @protected + * @type {Number} + * @default undefined + * @todo Should the width and height be protected? + **/ + this.height = undefined; + + /** + * Horizontal position of the cache relative to the target's origin. + * @property x + * @protected + * @type {Number} + * @default undefined + **/ + this.x = undefined; + + /** + * Vertical position of the cache relative to target's origin. + * @property y + * @protected + * @type {Number} + * @default undefined + **/ + this.y = undefined; + + /** + * The internal scale of the cache image, does not affects display size. This is useful to both increase and + * decrease render quality. Objects with increased scales are more likely to look good when scaled up or rotated. + * Objects with decreased scales can save on rendering performance. + * @property scale + * @protected + * @type {Number} + * @default 1 + **/ + this.scale = 1; + + /** + * The x offset used for drawing into the cache itself, accounts for both transforms applied. + * @property offX + * @protected + * @type {Number} + * @default 0 + **/ + this.offX = 0; + + /** + * The y offset used for drawing into the cache itself, accounts for both transforms applied. + * @property offY + * @protected + * @type {Number} + * @default 0 + **/ + this.offY = 0; + + /** + * Track how many times the cache has been updated, mostly used for preventing duplicate cacheURLs. + * This can be useful to see if a cache has been updated. + * @property cacheID + * @type {Number} + * @default 0 + **/ + this.cacheID = 0; + + // protected: + /** + * The relative offset of the filter's x position, used for drawing the cache onto its container. + * Re-calculated every update call before drawing. + * @property _filterOffY + * @protected + * @type {Number} + * @default 0 + **/ + this._filterOffX = 0; + + /** + * The relative offset of the filter's y position, used for drawing the cache onto its container. + * Re-calculated every update call before drawing. + * @property _filterOffY + * @protected + * @type {Number} + * @default 0 + **/ + this._filterOffY = 0; - /** - * Returns a matrix based on this object's current transform. - * @method getMatrix - * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new - * Matrix object is returned. - * @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); - }; + /** + * The cacheID when a DataURL was requested. + * @property _cacheDataURLID + * @protected + * @type {Number} + * @default 0 + **/ + this._cacheDataURLID = 0; + + /** + * The cache's DataURL, generated on-demand using the getter. + * @property _cacheDataURL + * @protected + * @type {String} + * @default null + **/ + this._cacheDataURL = null; + + /** + * Internal tracking of final bounding width, approximately width*scale; however, filters can complicate the actual value. + * @property _drawWidth + * @protected + * @type {Number} + * @default 0 + **/ + this._drawWidth = 0; + + /** + * Internal tracking of final bounding height, approximately height*scale; however, filters can complicate the actual value. + * @property _drawHeight + * @protected + * @type {Number} + * @default 0 + **/ + this._drawHeight = 0; + } + var p = BitmapCache.prototype; /** - * Generates a Matrix2D object representing the combined transform of the display object and all of its - * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can - * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} - * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}. - * @method getConcatenatedMatrix - * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values. - * If null, a new Matrix2D object is returned. - * @return {Matrix2D} The combined matrix. + * Returns the bounds that surround all applied filters, relies on each filter to describe how it changes bounds. + * @method getFilterBounds + * @param {DisplayObject} target The object to check the filter bounds for. + * @param {Rectangle} [output=null] Optional parameter, if provided then calculated bounds will be applied to that object. + * @return {Rectangle} bounds object representing the bounds with filters. + * @static **/ - p.getConcatenatedMatrix = function(matrix) { - var o = this, mtx = this.getMatrix(matrix); - while (o = o.parent) { - mtx.prependMatrix(o.getMatrix(o._props.matrix)); + BitmapCache.getFilterBounds = function(target, output) { + if(!output){ output = new createjs.Rectangle(); } + var filters = target.filters; + var filterCount = filters && filters.length; + if (!!filterCount <= 0) { return output; } + + for(var i=0; i 0 at - * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object. + * Actually create the correct cache surface and properties associated with it. Caching and it's benefits are discussed + * by the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} function and this class description. Here are the detailed + * specifics of how to use the options object. * - *

    Example

    + * - If options.useGL is set to "new" a StageGL is created and contained on this for use when rendering the cache. + * - If options.useGL is set to "stage" if the current stage is a StageGL it will be used. If not then it will default to "new". + * - If options.useGL is a StageGL instance it will not create one but use the one provided. + * - If options.useGL is undefined a Context 2D cache will be performed. * - * stage.addEventListener("stagemousedown", handleMouseDown); - * function handleMouseDown(event) { - * var hit = myShape.hitTest(event.stageX, event.stageY); - * } + * This means you can use any combination of StageGL and 2D with either, neither, or both the stage and cache being + * WebGL. Using "new" with a StageGL display list is highly unrecommended, but still an option. It should be avoided + * due to negative performance reasons and the Image loading limitation noted in the class complications above. * - * Please note that shape-to-shape collision is not currently supported by EaselJS. - * @method hitTest - * @param {Number} x The x position to check in the display object's local coordinates. - * @param {Number} y The y position to check in the display object's local coordinates. - * @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified - * local Point. - */ - p.hitTest = function(x, y) { - var ctx = DisplayObject._hitTestContext; - ctx.setTransform(1, 0, 0, 1, -x, -y); - this.draw(ctx); + * When "options.useGL" is set to the parent stage of the target and WebGL, performance is increased by using + * "RenderTextures" instead of canvas elements. These are internal Textures on the graphics card stored in the GPU. + * Because they are no longer canvases you cannot perform operations you could with a regular canvas. The benefit + * is that this avoids the slowdown of copying the texture back and forth from the GPU to a Canvas element. + * This means "stage" is the recommended option when available. + * + * A StageGL cache does not infer the ability to draw objects a StageGL cannot currently draw, i.e. do not use a + * WebGL context cache when caching a Shape, Text, etc. + *

    WebGL cache with a 2D context

    + * + * var stage = new createjs.Stage(); + * var bmp = new createjs.Bitmap(src); + * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "new"}); // no StageGL to use, so make one + * + * var shape = new createjs.Shape(); + * shape.graphics.clear().fill("red").drawRect(0,0,20,20); + * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache + * + *

    WebGL cache with a WebGL context

    + * + * var stageGL = new createjs.StageGL(); + * var bmp = new createjs.Bitmap(src); + * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "stage"}); // use our StageGL to cache + * + * var shape = new createjs.Shape(); + * shape.graphics.clear().fill("red").drawRect(0,0,20,20); + * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache + * + * You may wish to create your own StageGL instance to control factors like clear color, transparency, AA, and + * others. If you do, pass a new instance in instead of "true", the library will automatically set the + * {{#crossLink "StageGL/isCacheControlled"}}{{/crossLink}} to true on your instance. This will trigger it to behave + * correctly, and not assume your main context is WebGL. + * + * @public + * @method BitmapCache.cache + * @param {Number} x The x coordinate origin for the cache region. + * @param {Number} y The y coordinate origin for the cache region. + * @param {Number} width The width of the cache region. + * @param {Number} height The height of the cache region. + * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape + * using myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and + * rotate cached elements with greater fidelity. Default is 1. + * @param {Object} [options=undefined] Specify additional parameters for the cache logic + * @param {undefined|"new"|"stage"|StageGL} [options.useGL=undefined] Select whether to use context 2D, or WebGL rendering, and + * whether to make a new stage instance or use an existing one. See above for extensive details on use. + * @for BitmapCache + */ + p.define = function(target, x, y, width, height, scale, options) { + if(!target){ throw "No symbol to cache"; } + this._options = options; + this.target = target; - var hit = this._testHit(ctx); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, 2, 2); - return hit; + this.width = width >= 1 ? width : 1; + this.height = height >= 1 ? height : 1; + this.x = x || 0; + this.y = y || 0; + this.scale = scale || 1; + + this.update(); }; /** - * Provides a chainable shortcut method for setting a number of properties on the instance. - * - *

    Example

    - * - * var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25); - * var shape = stage.addChild(new Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5}); - * - * @method set - * @param {Object} props A generic object containing properties to copy to the DisplayObject instance. - * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.) - * @chainable - */ - p.set = function(props) { - for (var n in props) { this[n] = props[n]; } - return this; + * Directly called via {{#crossLink "DisplayObject/updateCache:method"}}{{/crossLink}}, but also internally. This + * has the dual responsibility of making sure the surface is ready to be drawn to, and performing the draw. For + * full details of each behaviour, check the protected functions {{#crossLink "BitmapCache/_updateSurface"}}{{/crossLink}} + * and {{#crossLink "BitmapCache/_drawToCache"}}{{/crossLink}} respectively. + * @method update + * @param {String} [compositeOperation=null] The DisplayObject this cache is linked to. + **/ + p.update = function(compositeOperation) { + if(!this.target) { throw "define() must be called before update()"; } + + var filterBounds = BitmapCache.getFilterBounds(this.target); + var surface = this.target.cacheCanvas; + + this._drawWidth = Math.ceil(this.width*this.scale) + filterBounds.width; + this._drawHeight = Math.ceil(this.height*this.scale) + filterBounds.height; + + if(!surface || this._drawWidth != surface.width || this._drawHeight != surface.height) { + this._updateSurface(); + } + + this._filterOffX = filterBounds.x; + this._filterOffY = filterBounds.y; + this.offX = this.x*this.scale + this._filterOffX; + this.offY = this.y*this.scale + this._filterOffY; + + this._drawToCache(compositeOperation); + + this.cacheID = this.cacheID?this.cacheID+1:1; }; /** - * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation). - * Objects that have been cached will return the bounds of the cache. - * - * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use - * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container - * bounds. - * - * - * - * - * - * - * - * - * - *
    All - * All display objects support setting bounds manually using setBounds(). Likewise, display objects that - * have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override - * the automatic calculations listed below. - *
    Bitmap - * Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0). - *
    Sprite - * Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified - * in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} - *
    Container - * Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds(). - *
    Shape - * Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds. - *
    Text - * Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are - * not, especially when using textBaseline values other than "top". - *
    BitmapText - * Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close - * to (x=0,y=0). - *
    - * - * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and - * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the - * bounds explicitly: - * - * var bounds = obj.getBounds(); - * obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); - * // getBounds will now use the set values, instead of recalculating - * - * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its - * values if you need to retain it. - * - * var myBounds = obj.getBounds().clone(); - * // OR: - * myRect.copy(obj.getBounds()); - * - * @method getBounds - * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this - * object. + * Reset and release all the properties and memory associated with this cache. + * @method release **/ - p.getBounds = function() { - if (this._bounds) { return this._rectangle.copy(this._bounds); } - var cacheCanvas = this.cacheCanvas; - if (cacheCanvas) { - var scale = this._cacheScale; - return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale); + 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); } + } + // set the context to none and let the garbage collector get the rest when the canvas itself gets removed + this._webGLCache = false; + } else { + var stage = this.target.stage; + if (stage instanceof createjs.StageGL){ + stage.releaseTexture(this.target.cacheCanvas); + } } - return null; + + this.target = this.target.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; }; /** - * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied). - * Objects that have been cached will return the transformed bounds of the cache. - * - * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use - * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container - * bounds. - * - * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its - * values if you need to retain it. - * - * Container instances calculate aggregate bounds for all children that return bounds via getBounds. - * @method getTransformedBounds - * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object. + * Returns a data URL for the cache, or `null` if this display object is not cached. + * Uses {{#crossLink "BitmapCache/cacheID:property"}}{{/crossLink}} to ensure a new data URL is not generated if the + * cache has not changed. + * @method getCacheDataURL + * @return {String} The image data url for the cache. **/ - p.getTransformedBounds = function() { - return this._getBounds(); + p.getCacheDataURL = function() { + var cacheCanvas = this.target && this.target.cacheCanvas; + if (!cacheCanvas) { return null; } + if (this.cacheID != this._cacheDataURLID) { + this._cacheDataURLID = this.cacheID; + this._cacheDataURL = cacheCanvas.toDataURL?cacheCanvas.toDataURL():null; // incase function is + } + return this._cacheDataURL; }; /** - * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape & - * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always - * override calculated bounds. - * - * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance - * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50). - * @method setBounds - * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds. - * @param {Number} y The y origin of the bounds. - * @param {Number} width The width of the bounds. - * @param {Number} height The height of the bounds. + * Use context2D drawing commands to display the cache canvas being used. + * @method draw + * @param {CanvasRenderingContext2D} ctx The context to draw into. + * @return {Boolean} Whether the draw was handled successfully. **/ - p.setBounds = function(x, y, width, height) { - if (x == null) { this._bounds = x; return; } - this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height); + p.draw = function(ctx) { + if(!this.target) { return false; } + ctx.drawImage(this.target.cacheCanvas, + this.x + (this._filterOffX/this.scale), this.y + (this._filterOffY/this.scale), + this._drawWidth/this.scale, this._drawHeight/this.scale + ); + return true; + }; + +// private methods: + /** + * Create or resize the invisible canvas/surface that is needed for the display object(s) to draw to, + * and in turn be used in their stead when drawing. The surface is resized to the size defined + * by the width and height, factoring in scaling and filters. Adjust them to adjust the output size. + * @method _updateSurface + * @protected + **/ + p._updateSurface = function() { + if (!this._options || !this._options.useGL) { + var surface = this.target.cacheCanvas; + + // create it if it's missing + if(!surface) { + surface = this.target.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); + } + + // now size it + surface.width = this._drawWidth; + surface.height = this._drawHeight; + return; + } + + // create it if it's missing + if (!this._webGLCache) { + if (this._options.useGL === "stage") { + if(!(this.target.stage && this.target.stage.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."; + throw error; + } + this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks + this._webGLCache = this.target.stage; + + } 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 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 { + throw "Invalid option provided to useGL, expected ['stage', 'new', StageGL, undefined], got "+ this._options.useGL; + } + } + + // now size render surfaces + var surface = this.target.cacheCanvas; + var stageGL = this._webGLCache; + + // if we have a dedicated stage we've gotta size it + if (stageGL.isCacheControlled) { + 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); + } + } }; /** - * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are - * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements - * are copied by reference (masks, individual filter instances, hit area) - * @method clone - * @return {DisplayObject} A clone of the current DisplayObject instance. + * Perform the cache draw out for context 2D now that the setup properties have been performed. + * @method _drawToCache + * @protected **/ - p.clone = function() { - return this._cloneProps(new DisplayObject()); + p._drawToCache = function(compositeOperation) { + var surface = this.target.cacheCanvas; + var target = this.target; + var webGL = this._webGLCache; + + if (webGL){ + //TODO: auto split blur into an x/y pass + 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; + + surface.width = this._drawWidth; + surface.height = this._drawHeight; + } else { + var ctx = surface.getContext("2d"); + + if (!compositeOperation) { + ctx.clearRect(0, 0, this._drawWidth+1, this._drawHeight+1); + } + + ctx.save(); + ctx.globalCompositeOperation = compositeOperation; + ctx.setTransform(this.scale,0,0,this.scale, -this._filterOffX,-this._filterOffY); + ctx.translate(-this.x, -this.y); + target.draw(ctx, true); + ctx.restore(); + + + if (target.filters && target.filters.length) { + this._applyFilters(ctx); + } + } + surface._invalid = true; }; /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. + * Work through every filter and apply its individual visual transformation. + * @method _applyFilters + * @protected **/ - p.toString = function() { - return "[DisplayObject (name="+ this.name +")]"; + p._applyFilters = function(ctx) { + var filters = this.target.filters; + + var w = this._drawWidth; + var h = this._drawHeight; + + var data; + + 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) { + ctx.putImageData(data, 0,0); + data = null; + } + filter.applyFilter(ctx, 0,0, w,h); + } else { + if(!data) { + data = ctx.getImageData(0,0, w,h); + } + filter._applyFilter(data); + } + + // work through the multipass if it's there, otherwise move on + filter = filter._multiPass !== null ? filter._multiPass : filters[++i]; + } while (filter); + + //done + if(data) { + ctx.putImageData(data, 0,0); + } }; + createjs.BitmapCache = BitmapCache; +}()); -// private methods: +//############################################################################## +// BlurFilter.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: /** - * Called before the object gets drawn and is a chance to ensure the display state of the object is correct. - * Mostly used by {{#crossLink "MovieClip"}}{{/crossLink}} and {{#crossLink "BitmapText"}}{{/crossLink}} to - * correct their internal state and children prior to being drawn. + * Applies a box blur to DisplayObjects in context 2D and a Gaussian blur in webgl. Note that this filter is fairly + * intensive, particularly if the quality is set higher than 1. * - * Is manually called via draw in a {{#crossLink "Stage"}}{{/crossLink}} but is automatically called when - * present in a {{#crossLink "StageGL"}}{{/crossLink}} instance. + *

    Example

    + * This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}} + * method to account for the spread that the blur causes. * - * @method _updateState - * @default null - */ - p._updateState = null; - - // separated so it can be used more easily in subclasses: - /** - * @method _cloneProps - * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject - * instance copied into. - * @return {DisplayObject} o - * @protected + * var shape = new createjs.Shape().set({x:100,y:100}); + * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); + * + * var blurFilter = new createjs.BlurFilter(5, 5, 1); + * shape.filters = [blurFilter]; + * var bounds = blurFilter.getBounds(); + * + * shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height); + * + * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. + * @class BlurFilter + * @extends Filter + * @constructor + * @param {Number} [blurX=0] The horizontal blur radius in pixels. + * @param {Number} [blurY=0] The vertical blur radius in pixels. + * @param {Number} [quality=1] The number of blur iterations. **/ - p._cloneProps = function(o) { - o.alpha = this.alpha; - o.mouseEnabled = this.mouseEnabled; - o.tickEnabled = this.tickEnabled; - o.name = this.name; - o.regX = this.regX; - o.regY = this.regY; - o.rotation = this.rotation; - o.scaleX = this.scaleX; - o.scaleY = this.scaleY; - o.shadow = this.shadow; - o.skewX = this.skewX; - o.skewY = this.skewY; - o.visible = this.visible; - o.x = this.x; - o.y = this.y; - o.compositeOperation = this.compositeOperation; - o.snapToPixel = this.snapToPixel; - o.filters = this.filters==null?null:this.filters.slice(0); - o.mask = this.mask; - o.hitArea = this.hitArea; - o.cursor = this.cursor; - o._bounds = this._bounds; - return o; - }; + function BlurFilter( blurX, blurY, quality) { + this.Filter_constructor(); - /** - * @method _applyShadow - * @protected - * @param {CanvasRenderingContext2D} ctx - * @param {Shadow} shadow - **/ - p._applyShadow = function(ctx, shadow) { - shadow = shadow || Shadow.identity; - ctx.shadowColor = shadow.color; - ctx.shadowOffsetX = shadow.offsetX; - ctx.shadowOffsetY = shadow.offsetY; - ctx.shadowBlur = shadow.blur; + // public properties: + /** + * Horizontal blur radius in pixels + * @property blurX + * @default 0 + * @type Number + **/ + this._blurX = blurX; + this._blurXTable = []; + this._lastBlurX = null; + + /** + * Vertical blur radius in pixels + * @property blurY + * @default 0 + * @type Number + **/ + this._blurY = blurY; + this._blurYTable = []; + this._lastBlurY = null; + + /** + * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a + * smoother blur, but take twice as long to run. + * @property quality + * @default 1 + * @type Number + **/ + this._quality; + this._lastQuality = null; + + /** + * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}} + */ + this.FRAG_SHADER_TEMPLATE = ( + "uniform float xWeight[{{blurX}}];" + + "uniform float yWeight[{{blurY}}];" + + "uniform vec2 textureOffset;" + + "void main(void) {" + + "vec4 color = vec4(0.0);" + + + "float xAdj = ({{blurX}}.0-1.0)/2.0;" + + "float yAdj = ({{blurY}}.0-1.0)/2.0;" + + "vec2 sampleOffset;" + + + "for(int i=0; i<{{blurX}}; i++) {" + + "for(int j=0; j<{{blurY}}; j++) {" + + "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" + + "color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" + + "}" + + "}" + + + "gl_FragColor = color.rgba;" + + "}" + ); + + // update the filter using the setters + if(isNaN(quality) || quality < 1){ quality = 1; } + this.setQuality(quality|0); + } + var p = createjs.extend(BlurFilter, createjs.Filter); + + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. + + p.getBlurX = function() { return this._blurX; }; + p.getBlurY = function() { return this._blurY; }; + p.setBlurX = function(value) { + if(isNaN(value) || value < 0){ value = 0; } + this._blurX = value; + }; + p.setBlurY = function(value) { + if(isNaN(value) || value < 0){ value = 0; } + this._blurY = value; }; + p.getQuality = function() { return this._quality; }; + p.setQuality = function(value) { + if(isNaN(value) || value < 0){ value = 0; } + this._quality = value | 0; + }; + p._getShader = function() { + var xChange = this._lastBlurX !== this._blurX; + var yChange = this._lastBlurY !== this._blurY; + var qChange = this._lastQuality !== this._quality; + if(xChange || yChange || qChange) { + if(xChange || qChange) { this._blurXTable = this._getTable(this._blurX * this._quality); } + if(yChange || qChange) { this._blurYTable = this._getTable(this._blurY * this._quality); } + this._updateShader(); + this._lastBlurX = this._blurX; + this._lastBlurY = this._blurY; + this._lastQuality = this._quality; + return undefined; // force a rebuild + } + return this._compiledShader; + }; + p._setShader = function() { this._compiledShader; }; + + try { + Object.defineProperties(p, { + blurX: { get: p.getBlurX, set: p.setBlurX }, + blurY: { get: p.getBlurY, set: p.setBlurY }, + quality: { get: p.getQuality, set: p.setQuality }, + _builtShader: { get: p._getShader, set: p._setShader} + }); + } catch (e) { console.log(e); } /** - * @method _tick - * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. - * @protected - **/ - p._tick = function(evtObj) { - // because tick can be really performance sensitive, check for listeners before calling dispatchEvent. - var ls = this._listeners; - if (ls && ls["tick"]) { - // reset & reuse the event object to avoid construction / GC costs: - evtObj.target = null; - evtObj.propagationStopped = evtObj.immediatePropagationStopped = false; - this.dispatchEvent(evtObj); + * Internal lookup function to create gaussian distribution. + * @method _getTable + * @param {Number} spread How many steps in the curve. + * @return {Array} An array with Math.ceil(spread*2) entries with appropriately distributed weights. + */ + p._getTable = function(spread) { + var EDGE = 4.2; + if(spread<=1) { return [1]; } + + var result = []; + var count = Math.ceil(spread*2); + count += (count%2)?0:1; + var adjust = (count/2)|0; + for(var i = -adjust; i<=adjust; i++) { + var x = (i/adjust)*EDGE; + result.push(1/Math.sqrt(2*Math.PI) * Math.pow(Math.E, -(Math.pow(x,2)/4))); } + var factor = result.reduce(function(a, b) { return a + b; }); + return result.map(function(currentValue, index, array) { return currentValue/factor; }); }; /** - * @method _testHit - * @protected - * @param {CanvasRenderingContext2D} ctx - * @return {Boolean} - **/ - p._testHit = function(ctx) { - try { - var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1; - } catch (e) { - if (!DisplayObject.suppressCrossDomainErrors) { - throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images."; - } - } - return hit; + * Internal update function to create shader properties. + * @method _updateShader + */ + p._updateShader = function() { + if(this._blurX === undefined || this._blurY === undefined){ return; } + var result = this.FRAG_SHADER_TEMPLATE; + result = result.replace(/\{\{blurX\}\}/g, (this._blurXTable.length).toFixed(0)); + result = result.replace(/\{\{blurY\}\}/g, (this._blurYTable.length).toFixed(0)); + this.FRAG_SHADER_BODY = result; }; + /** docced in super class **/ + p.shaderParamSetup = function(gl, stage, shaderProgram) { + // load the normalized gaussian weight tables + gl.uniform1fv( + gl.getUniformLocation(shaderProgram, "xWeight"), + this._blurXTable + ); + gl.uniform1fv( + gl.getUniformLocation(shaderProgram, "yWeight"), + this._blurYTable + ); + + // what is the size of a single pixel in -1, 1 (webGL) space + gl.uniform2f( + gl.getUniformLocation(shaderProgram, "textureOffset"), + 2/(stage._viewportWidth*this._quality), 2/(stage._viewportHeight*this._quality) + ); + }; + +// constants: /** - * @method _getBounds - * @param {Matrix2D} matrix - * @param {Boolean} ignoreTransform If true, does not apply this object's transform. - * @return {Rectangle} + * Array of multiply values for blur calculations. + * @property MUL_TABLE + * @type Array * @protected + * @static **/ - p._getBounds = function(matrix, ignoreTransform){ - return this._transformBounds(this.getBounds(), matrix, ignoreTransform); - }; + BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1]; /** - * @method _transformBounds - * @param {Rectangle} bounds - * @param {Matrix2D} matrix - * @param {Boolean} ignoreTransform - * @return {Rectangle} + * Array of shift values for blur calculations. + * @property SHG_TABLE + * @type Array * @protected + * @static **/ - p._transformBounds = function(bounds, matrix, ignoreTransform) { - if (!bounds) { return bounds; } - var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix; - mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx); - - if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this. - if (matrix) { mtx.prependMatrix(matrix); } - - var x_a = width*mtx.a, x_b = width*mtx.b; - var y_c = height*mtx.c, y_d = height*mtx.d; - var tx = mtx.tx, ty = mtx.ty; - - var minX = tx, maxX = tx, minY = ty, maxY = ty; + BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9]; - if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } - if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } - if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; } +// public methods: + /** docced in super class **/ + p.getBounds = function (rect) { + var x = this.blurX|0, y = this.blurY| 0; + if(x <= 0 && y <= 0) { return rect; } + var q = Math.pow(this.quality, 0.2); + return (rect || new createjs.Rectangle()).pad(y*q+1,x*q+1,y*q+1,x*q+1); + }; + + /** docced in super class **/ + p.clone = function() { + return new BlurFilter(this.blurX, this.blurY, this.quality); + }; + + /** docced in super class **/ + p.toString = function() { + return "[BlurFilter]"; + }; + + +// private methods: + + /** docced in super class **/ + p._applyFilter = function (imageData) { + var radiusX = this._blurX >> 1; + if (isNaN(radiusX) || radiusX < 0) return false; + var radiusY = this._blurY >> 1; + if (isNaN(radiusY) || radiusY < 0) return false; + if (radiusX == 0 && radiusY == 0) return false; + + var iterations = this.quality; + if (isNaN(iterations) || iterations < 1) iterations = 1; + iterations |= 0; + if (iterations > 3) iterations = 3; + if (iterations < 1) iterations = 1; + + var px = imageData.data; + var x=0, y=0, i=0, p=0, yp=0, yi=0, yw=0, r=0, g=0, b=0, a=0, pr=0, pg=0, pb=0, pa=0; + + var divx = (radiusX + radiusX + 1) | 0; + var divy = (radiusY + radiusY + 1) | 0; + var w = imageData.width | 0; + var h = imageData.height | 0; + + var w1 = (w - 1) | 0; + var h1 = (h - 1) | 0; + var rxp1 = (radiusX + 1) | 0; + var ryp1 = (radiusY + 1) | 0; + + var ssx = {r:0,b:0,g:0,a:0}; + var sx = ssx; + for ( i = 1; i < divx; i++ ) + { + sx = sx.n = {r:0,b:0,g:0,a:0}; + } + sx.n = ssx; + + var ssy = {r:0,b:0,g:0,a:0}; + var sy = ssy; + for ( i = 1; i < divy; i++ ) + { + sy = sy.n = {r:0,b:0,g:0,a:0}; + } + sy.n = ssy; + + var si = null; + + + var mtx = BlurFilter.MUL_TABLE[radiusX] | 0; + var stx = BlurFilter.SHG_TABLE[radiusX] | 0; + var mty = BlurFilter.MUL_TABLE[radiusY] | 0; + var sty = BlurFilter.SHG_TABLE[radiusY] | 0; + + while (iterations-- > 0) { + + yw = yi = 0; + var ms = mtx; + var ss = stx; + for (y = h; --y > -1;) { + r = rxp1 * (pr = px[(yi) | 0]); + g = rxp1 * (pg = px[(yi + 1) | 0]); + b = rxp1 * (pb = px[(yi + 2) | 0]); + a = rxp1 * (pa = px[(yi + 3) | 0]); + + sx = ssx; + + for( i = rxp1; --i > -1; ) + { + sx.r = pr; + sx.g = pg; + sx.b = pb; + sx.a = pa; + sx = sx.n; + } + + for( i = 1; i < rxp1; i++ ) + { + p = (yi + ((w1 < i ? w1 : i) << 2)) | 0; + r += ( sx.r = px[p]); + g += ( sx.g = px[p+1]); + b += ( sx.b = px[p+2]); + a += ( sx.a = px[p+3]); + + sx = sx.n; + } + + si = ssx; + for ( x = 0; x < w; x++ ) + { + px[yi++] = (r * ms) >>> ss; + px[yi++] = (g * ms) >>> ss; + px[yi++] = (b * ms) >>> ss; + px[yi++] = (a * ms) >>> ss; + + p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2); + + r -= si.r - ( si.r = px[p]); + g -= si.g - ( si.g = px[p+1]); + b -= si.b - ( si.b = px[p+2]); + a -= si.a - ( si.a = px[p+3]); + + si = si.n; + + } + yw += w; + } + + ms = mty; + ss = sty; + for (x = 0; x < w; x++) { + yi = (x << 2) | 0; + + r = (ryp1 * (pr = px[yi])) | 0; + g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0; + b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0; + a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0; + + sy = ssy; + for( i = 0; i < ryp1; i++ ) + { + sy.r = pr; + sy.g = pg; + sy.b = pb; + sy.a = pa; + sy = sy.n; + } + + yp = w; + + for( i = 1; i <= radiusY; i++ ) + { + yi = ( yp + x ) << 2; + + r += ( sy.r = px[yi]); + g += ( sy.g = px[yi+1]); + b += ( sy.b = px[yi+2]); + a += ( sy.a = px[yi+3]); + + sy = sy.n; + + if( i < h1 ) + { + yp += w; + } + } + + yi = x; + si = ssy; + if ( iterations > 0 ) + { + for ( y = 0; y < h; y++ ) + { + p = yi << 2; + px[p+3] = pa =(a * ms) >>> ss; + if ( pa > 0 ) + { + px[p] = ((r * ms) >>> ss ); + px[p+1] = ((g * ms) >>> ss ); + px[p+2] = ((b * ms) >>> ss ); + } else { + px[p] = px[p+1] = px[p+2] = 0 + } + + p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2; + + r -= si.r - ( si.r = px[p]); + g -= si.g - ( si.g = px[p+1]); + b -= si.b - ( si.b = px[p+2]); + a -= si.a - ( si.a = px[p+3]); + + si = si.n; + + yi += w; + } + } else { + for ( y = 0; y < h; y++ ) + { + p = yi << 2; + px[p+3] = pa =(a * ms) >>> ss; + if ( pa > 0 ) + { + pa = 255 / pa; + px[p] = ((r * ms) >>> ss ) * pa; + px[p+1] = ((g * ms) >>> ss ) * pa; + px[p+2] = ((b * ms) >>> ss ) * pa; + } else { + px[p] = px[p+1] = px[p+2] = 0 + } + + p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2; + + r -= si.r - ( si.r = px[p]); + g -= si.g - ( si.g = px[p+1]); + b -= si.b - ( si.b = px[p+2]); + a -= si.a - ( si.a = px[p+3]); - if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } - if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } - if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; } + si = si.n; - return bounds.setValues(minX, minY, maxX-minX, maxY-minY); - }; + yi += w; + } + } + } - /** - * Indicates whether the display object has any mouse event listeners or a cursor. - * @method _isMouseOpaque - * @return {Boolean} - * @protected - **/ - p._hasMouseEventListener = function() { - var evts = DisplayObject._MOUSE_EVENTS; - for (var i= 0, l=evts.length; itransform and alpha properties concatenated with their parent - * Container. - * - * For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with x=50 - * and alpha=0.7 will be rendered to the canvas at x=150 and alpha=0.35. - * Containers have some overhead, so you generally shouldn't create a Container to hold a single child. - * - *

    Example

    - * - * var container = new createjs.Container(); - * container.addChild(bitmapInstance, shapeInstance); - * container.x = 100; - * - * @class Container - * @extends DisplayObject - * @constructor - **/ - function Container() { - this.DisplayObject_constructor(); - + /** + * Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will + * be copied from the red channel of the map, and the RGB channels will be copied from the target. + * + * Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much + * better performance. + * + *

    Example

    + * This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image. + * + * var box = new createjs.Shape(); + * box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100) + * box.graphics.drawRect(0, 0, 100, 100); + * box.cache(0, 0, 100, 100); + * + * var bmp = new createjs.Bitmap("path/to/image.jpg"); + * bmp.filters = [ + * new createjs.AlphaMapFilter(box.cacheCanvas) + * ]; + * bmp.cache(0, 0, 100, 100); + * stage.addChild(bmp); + * + * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters. + * @class AlphaMapFilter + * @extends Filter + * @constructor + * @param {HTMLImageElement|HTMLCanvasElement} 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(); + // public properties: /** - * The array of children in the display list. You should usually use the child management methods such as - * {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}}, - * {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is - * included for advanced uses. - * @property children - * @type Array - * @default null + * The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same + * dimensions as the target. + * @property alphaMap + * @type HTMLImageElement|HTMLCanvasElement **/ - this.children = []; + this.alphaMap = alphaMap; + + // private properties: /** - * Indicates whether the children of this container are independently enabled for mouse/pointer interaction. - * If false, the children will be aggregated under the container - for example, a click on a child shape would - * trigger a click event on the container. - * @property mouseChildren - * @type Boolean - * @default true + * @property _alphaMap + * @protected + * @type HTMLImageElement|HTMLCanvasElement **/ - this.mouseChildren = true; + this._alphaMap = null; /** - * If false, the tick will not be propagated to children of this Container. This can provide some performance benefits. - * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates - * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling). - * @property tickChildren - * @type Boolean - * @default true + * @property _mapData + * @protected + * @type Uint8ClampedArray **/ - this.tickChildren = true; + this._mapData = null; + this._mapTexture = null; + + this.FRAG_SHADER_BODY = ( + "uniform sampler2D uAlphaSampler;"+ + + "void main(void) {" + + "vec4 color = texture2D(uSampler, vRenderCoord);" + + "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)));" + + "}" + ); } - var p = createjs.extend(Container, createjs.DisplayObject); - - -// getter / setters: - /** - * Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead. - * @method _getNumChildren - * @protected - * @return {Number} - **/ - p._getNumChildren = function() { - return this.children.length; - }; - // Container.getNumChildren is @deprecated. Remove for 1.1+ - p.getNumChildren = createjs.deprecate(p._getNumChildren, "Container.getNumChildren"); + var p = createjs.extend(AlphaMapFilter, createjs.Filter); - /** - * Returns the number of children in the container. - * @property numChildren - * @type {Number} - * @readonly - **/ - try { - Object.defineProperties(p, { - numChildren: { get: p._getNumChildren } - }); - } catch (e) {} - + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. -// public methods: - /** - * Constructor alias for backwards compatibility. This method will be removed in future versions. - * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. - * @method initialize - * @deprecated in favour of `createjs.promote()` - **/ - p.initialize = Container; // TODO: deprecated. - - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var hasContent = this.cacheCanvas || this.children.length; - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); + /** 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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap); + + gl.uniform1i( + gl.getUniformLocation(shaderProgram, "uAlphaSampler"), + 1 + ); }; - /** - * Draws the display object 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) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - - // this ensures we don't have issues with display list changes that occur during a draw: - var list = this.children.slice(); - for (var i=0,l=list.length; iExample - * - * container.addChild(bitmapInstance); - * - * You can also add multiple children at once: - * - * container.addChild(bitmapInstance, shapeInstance, textInstance); - * - * @method addChild - * @param {DisplayObject} child The display object to add. - * @return {DisplayObject} The child that was added, or the last child if multiple children were added. - **/ - p.addChild = function(child) { - if (child == null) { return child; } - var l = arguments.length; - if (l > 1) { - for (var i=0; iExample - * - * addChildAt(child1, index); - * - * You can also add multiple children, such as: - * - * addChildAt(child1, child2, ..., index); - * - * The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list, - * you could use: - * - * container.addChildAt(myShape, container.getChildIndex(otherShape)); - * - * This would also bump otherShape's index up by one. Fails silently if the index is out of range. - * - * @method addChildAt - * @param {DisplayObject} child The display object to add. - * @param {Number} index The index to add the child at. - * @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added. - **/ - p.addChildAt = function(child, index) { - var l = arguments.length; - var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1] - if (indx < 0 || indx > this.children.length) { return arguments[l-2]; } - if (l > 2) { - for (var i=0; iExample - * - * container.removeChild(child); - * - * You can also remove multiple children: - * - * removeChild(child1, child2, ...); - * - * Returns true if the child (or children) was removed, or false if it was not in the display list. - * @method removeChild - * @param {DisplayObject} child The child to remove. - * @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list. + * @method _prepAlphaMap + * @protected **/ - p.removeChild = function(child) { - var l = arguments.length; - if (l > 1) { - var good = true; - for (var i=0; iExample + * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly. * - * container.removeChildAt(2); + *

    Example

    + * This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image. * - * You can also remove multiple children: + * var box = new createjs.Shape(); + * box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100) + * box.graphics.drawRect(0, 0, 100, 100); + * box.cache(0, 0, 100, 100); * - * container.removeChild(2, 7, ...) + * var bmp = new createjs.Bitmap("path/to/image.jpg"); + * bmp.filters = [ + * new createjs.AlphaMaskFilter(box.cacheCanvas) + * ]; + * bmp.cache(0, 0, 100, 100); * - * Returns true if the child (or children) was removed, or false if any index was out of range. - * @method removeChildAt - * @param {Number} index The index of the child to remove. - * @return {Boolean} true if the child (or children) was removed, or false if any index was out of range. + * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters. + * @class AlphaMaskFilter + * @extends Filter + * @constructor + * @param {HTMLImageElement|HTMLCanvasElement} mask **/ - p.removeChildAt = function(index) { - var l = arguments.length; - if (l > 1) { - var a = []; - for (var i=0; iExample + * Applies the filter to the specified context. * - * container.removeAllChildren(); - * - * @method removeAllChildren + * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters + * correctly. + * @method applyFilter + * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source. + * @param {Number} x The x position to use for the source rect. + * @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] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx. + * @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x. + * @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y. + * @return {Boolean} If the filter was applied successfully. **/ - p.removeAllChildren = function() { - var kids = this.children; - while (kids.length) { this._removeChildAt(0); } + p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) { + if (!this.mask) { return true; } + targetCtx = targetCtx || ctx; + if (targetX == null) { targetX = x; } + if (targetY == null) { targetY = y; } + + targetCtx.save(); + if (ctx != targetCtx) { + // TODO: support targetCtx and targetX/Y + // clearRect, then draw the ctx in? + return false; + } + + targetCtx.globalCompositeOperation = "destination-in"; + targetCtx.drawImage(this.mask, targetX, targetY); + targetCtx.restore(); + return true; + }; + + /** docced in super class **/ + p.clone = function () { + return new AlphaMaskFilter(this.mask); }; + /** docced in super class **/ + p.toString = function () { + return "[AlphaMaskFilter]"; + }; + + + createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter"); +}()); + +//############################################################################## +// ColorFilter.js +//############################################################################## + +this.createjs = this.createjs||{}; + +(function() { + "use strict"; + + +// constructor: /** - * Returns the child at the specified index. + * Applies a color transform to DisplayObjects. * *

    Example

    + * This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels + * to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel. * - * container.getChildAt(2); + * var shape = new createjs.Shape().set({x:100,y:100}); + * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); * - * @method getChildAt - * @param {Number} index The index of the child to return. - * @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index. + * shape.filters = [ + * new createjs.ColorFilter(0,0,0,1, 0,0,255,0) + * ]; + * shape.cache(-50, -50, 100, 100); + * + * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. + * @class ColorFilter + * @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1. + * @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1. + * @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1. + * @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1. + * @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range + * between -255 and 255. + * @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range + * between -255 and 255. + * @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range + * between -255 and 255. + * @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range + * between -255 and 255. + * @constructor + * @extends Filter **/ - p.getChildAt = function(index) { - return this.children[index]; - }; + function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) { + this.Filter_constructor(); + + // public properties: + /** + * Red channel multiplier. + * @property redMultiplier + * @type Number + **/ + this.redMultiplier = redMultiplier != null ? redMultiplier : 1; - /** - * Returns the child with the specified name. - * @method getChildByName - * @param {String} name The name of the child to return. - * @return {DisplayObject} The child with the specified name. - **/ - p.getChildByName = function(name) { - var kids = this.children; - for (var i=0,l=kids.length;iExample: Display children with a higher y in front. - * - * var sortFunction = function(obj1, obj2, options) { - * if (obj1.y > obj2.y) { return 1; } - * if (obj1.y < obj2.y) { return -1; } - * return 0; - * } - * container.sortChildren(sortFunction); - * - * @method sortChildren - * @param {Function} sortFunction the function to use to sort the child list. See JavaScript's Array.sort - * documentation for details. - **/ - p.sortChildren = function(sortFunction) { - this.children.sort(sortFunction); + /** docced in super class **/ + p.toString = function() { + return "[ColorFilter]"; + }; + + /** docced in super class **/ + p.clone = function() { + return new ColorFilter( + this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, + this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset + ); + }; + +// private methods: + /** docced in super class **/ + p._applyFilter = function(imageData) { + var data = imageData.data; + var l = data.length; + for (var i=0; iExample * - * var index = container.getChildIndex(child); + * myColorMatrix.adjustHue(20).adjustBrightness(50); * - * @method getChildIndex - * @param {DisplayObject} child The child to return the index of. - * @return {Number} The index of the specified child. -1 if the child is not found. + * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}} + * for an example of how to use ColorMatrix to change a DisplayObject's color. + * @class ColorMatrix + * @param {Number} brightness + * @param {Number} contrast + * @param {Number} saturation + * @param {Number} hue + * @constructor **/ - p.getChildIndex = function(child) { - return createjs.indexOf(this.children, child); - }; - + function ColorMatrix(brightness, contrast, saturation, hue) { + this.setColor(brightness, contrast, saturation, hue); + } + var p = ColorMatrix.prototype; + +// constants: /** - * Swaps the children at the specified indexes. Fails silently if either index is out of range. - * @method swapChildrenAt - * @param {Number} index1 - * @param {Number} index2 + * Array of delta values for contrast calculations. + * @property DELTA_INDEX + * @type Array + * @protected + * @static **/ - p.swapChildrenAt = function(index1, index2) { - var kids = this.children; - var o1 = kids[index1]; - var o2 = kids[index2]; - if (!o1 || !o2) { return; } - kids[index1] = o2; - kids[index2] = o1; - }; - + ColorMatrix.DELTA_INDEX = [ + 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, + 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, + 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, + 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, + 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, + 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, + 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, + 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, + 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, + 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, + 10.0 + ]; + /** - * Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this - * Container. - * @method swapChildren - * @param {DisplayObject} child1 - * @param {DisplayObject} child2 + * Identity matrix values. + * @property IDENTITY_MATRIX + * @type Array + * @protected + * @static **/ - p.swapChildren = function(child1, child2) { - var kids = this.children; - var index1,index2; - for (var i=0,l=kids.length;i= l) { return; } - for (var i=0;i 0 at the - * specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all - * transform properties including regX/Y. - * @method hitTest - * @param {Number} x The x position to check in the display object's local coordinates. - * @param {Number} y The y position to check in the display object's local coordinates. - * @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified - * coordinates. + * Adjusts the brightness 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} value A value between -255 & 255 that will be added to the RGB channels. + * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) + * @chainable **/ - p.hitTest = function(x, y) { - // TODO: optimize to use the fast cache check where possible. - return (this.getObjectUnderPoint(x, y) != null); + p.adjustBrightness = function(value) { + if (value == 0 || isNaN(value)) { return this; } + value = this._cleanValue(value,255); + this._multiplyMatrix([ + 1,0,0,0,value, + 0,1,0,0,value, + 0,0,1,0,value, + 0,0,0,1,0, + 0,0,0,0,1 + ]); + return this; }; /** - * Returns an array of all display objects under the specified coordinates that are in this container's display - * list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}} - * set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0. - * This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully. - * For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}), - * and only if the mouse's position has changed. - * - *
      - *
    • By default (mode=0) this method evaluates all display objects.
    • - *
    • By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}} - * and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.
    • - *
    • Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event - * listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects - * that would normally intercept mouse interaction will be included. This can significantly improve performance - * in some cases by reducing the number of display objects that need to be tested.
    • - * - * - * This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}. - * @method getObjectsUnderPoint - * @param {Number} x The x position in the container to test. - * @param {Number} y The y position in the container to test. - * @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects. - * @return {Array} An Array of DisplayObjects under the specified coordinates. + * Adjusts the contrast of pixel color. + * Positive values will increase contrast, negative values will decrease contrast. + * @method adjustContrast + * @param {Number} value A value between -100 & 100. + * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) + * @chainable **/ - p.getObjectsUnderPoint = function(x, y, mode) { - var arr = []; - var pt = this.localToGlobal(x, y); - this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1); - return arr; + p.adjustContrast = function(value) { + if (value == 0 || isNaN(value)) { return this; } + value = this._cleanValue(value,100); + var x; + if (value<0) { + x = 127+value/100*127; + } else { + x = value%1; + if (x == 0) { + x = ColorMatrix.DELTA_INDEX[value]; + } else { + x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity. + } + x = x*127+127; + } + this._multiplyMatrix([ + x/127,0,0,0,0.5*(127-x), + 0,x/127,0,0,0.5*(127-x), + 0,0,x/127,0,0.5*(127-x), + 0,0,0,1,0, + 0,0,0,0,1 + ]); + return this; }; /** - * Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display - * object. This runs significantly faster than getObjectsUnderPoint(), but is still potentially an expensive - * operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information. - * @method getObjectUnderPoint - * @param {Number} x The x position in the container to test. - * @param {Number} y The y position in the container to test. - * @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects. - * @return {DisplayObject} The top-most display object under the specified coordinates. + * Adjusts the color saturation of the pixel. + * Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale). + * @method adjustSaturation + * @param {Number} value A value between -100 & 100. + * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) + * @chainable **/ - p.getObjectUnderPoint = function(x, y, mode) { - var pt = this.localToGlobal(x, y); - return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1); - }; - - /** - * Docced in superclass. - */ - p.getBounds = function() { - return this._getBounds(null, true); + p.adjustSaturation = function(value) { + if (value == 0 || isNaN(value)) { return this; } + value = this._cleanValue(value,100); + var x = 1+((value > 0) ? 3*value/100 : value/100); + var lumR = 0.3086; + var lumG = 0.6094; + var lumB = 0.0820; + this._multiplyMatrix([ + lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0, + lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0, + lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0, + 0,0,0,1,0, + 0,0,0,0,1 + ]); + return this; }; - - + + /** - * Docced in superclass. - */ - p.getTransformedBounds = function() { - return this._getBounds(); + * Adjusts the hue of the pixel color. + * @method adjustHue + * @param {Number} value A value between -180 & 180. + * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) + * @chainable + **/ + p.adjustHue = function(value) { + if (value == 0 || isNaN(value)) { return this; } + value = this._cleanValue(value,180)/180*Math.PI; + var cosVal = Math.cos(value); + var sinVal = Math.sin(value); + var lumR = 0.213; + var lumG = 0.715; + var lumB = 0.072; + this._multiplyMatrix([ + lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0, + lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0, + lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0, + 0,0,0,1,0, + 0,0,0,0,1 + ]); + return this; }; /** - * Returns a clone of this Container. Some properties that are specific to this instance's current context are - * reverted to their defaults (for example .parent). - * @method clone - * @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the - * properties of the container will be cloned, but the new instance will not have any children. - * @return {Container} A clone of the current Container instance. + * Concatenates (multiplies) the specified matrix with this one. + * @method concat + * @param {Array} matrix An array or ColorMatrix instance. + * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) + * @chainable **/ - p.clone = function(recursive) { - var o = this._cloneProps(new Container()); - if (recursive) { this._cloneChildren(o); } - return o; + p.concat = function(matrix) { + matrix = this._fixMatrix(matrix); + if (matrix.length != ColorMatrix.LENGTH) { return this; } + this._multiplyMatrix(matrix); + return this; }; /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. + * Returns a clone of this ColorMatrix. + * @method clone + * @return {ColorMatrix} A clone of this ColorMatrix. **/ - p.toString = function() { - return "[Container (name="+ this.name +")]"; + p.clone = function() { + return (new ColorMatrix()).copy(this); }; - -// private methods: /** - * @method _tick - * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. - * @protected + * Return a length 25 (5x5) array instance containing this matrix's values. + * @method toArray + * @return {Array} An array holding this matrix's values. **/ - p._tick = function(evtObj) { - if (this.tickChildren) { - for (var i=this.children.length-1; i>=0; i--) { - var child = this.children[i]; - if (child.tickEnabled && child._tick) { child._tick(evtObj); } - } + p.toArray = function() { + var arr = []; + for (var i= 0, l=ColorMatrix.LENGTH; i this.children.length-1) { return false; } - var child = this.children[index]; - if (child) { child.parent = null; } - this.children.splice(index, 1); - if (!silent) { child.dispatchEvent("removed"); } - return true; + p.toString = function() { + return "[ColorMatrix]"; }; + +// private methods: /** - * @method _getObjectsUnderPoint - * @param {Number} x - * @param {Number} y - * @param {Array} arr - * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners. - * @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object. - * @param {Number} currentDepth Indicates the current depth of the search. - * @return {DisplayObject} + * @method _multiplyMatrix + * @param {Array} matrix * @protected **/ - p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) { - currentDepth = currentDepth || 0; - if (!currentDepth && !this._testMask(this, x, y)) { return null; } - var mtx, ctx = createjs.DisplayObject._hitTestContext; - activeListener = activeListener || (mouse&&this._hasMouseEventListener()); + p._multiplyMatrix = function(matrix) { + var i, j, k, col = []; - // draw children one at a time, and check if we get a hit: - var children = this.children, l = children.length; - for (var i=l-1; i>=0; i--) { - var child = children[i]; - var hitArea = child.hitArea; - if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; } - if (!hitArea && !this._testMask(child, x, y)) { continue; } - - // if a child container has a hitArea then we only need to check its hitAre2a, so we can treat it as a normal DO: - if (!hitArea && child instanceof Container) { - var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1); - if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; } - } else { - if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; } - - // TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense. - var props = child.getConcatenatedDisplayProps(child._props); - mtx = props.matrix; - - if (hitArea) { - mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix)); - props.alpha = hitArea.alpha; + for (i=0;i<5;i++) { + for (j=0;j<5;j++) { + col[j] = this[j+i*5]; + } + for (j=0;j<5;j++) { + var val=0; + for (k=0;k<5;k++) { + val += matrix[j+k*5]*col[k]; } - - ctx.globalAlpha = props.alpha; - ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y); - (hitArea||child).draw(ctx); - if (!this._testHit(ctx)) { continue; } - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, 2, 2); - if (arr) { arr.push(child); } - else { return (mouse && !this.mouseChildren) ? this : child; } + this[j+i*5] = val; } } - return null; - }; - - /** - * @method _testMask - * @param {DisplayObject} target - * @param {Number} x - * @param {Number} y - * @return {Boolean} Indicates whether the x/y is within the masked region. - * @protected - **/ - p._testMask = function(target, x, y) { - var mask = target.mask; - if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; } - - var mtx = this._props.matrix, parent = target.parent; - mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity(); - mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx); - - var ctx = createjs.DisplayObject._hitTestContext; - ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y); - - // draw the mask as a solid fill: - mask.graphics.drawAsPath(ctx); - ctx.fillStyle = "#000"; - ctx.fill(); - - if (!this._testHit(ctx)) { return false; } - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, 2, 2); - - return true; }; - + /** - * @method _getBounds - * @param {Matrix2D} matrix - * @param {Boolean} ignoreTransform If true, does not apply this object's transform. - * @return {Rectangle} + * Make sure values are within the specified range, hue has a limit of 180, brightness is 255, others are 100. + * @method _cleanValue + * @param {Number} value The raw number + * @param {Number} limit The maximum that the number can be. The minimum is the limit * -1. * @protected **/ - p._getBounds = function(matrix, ignoreTransform) { - var bounds = this.DisplayObject_getBounds(); - if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); } - - var mtx = this._props.matrix; - mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx); - if (matrix) { mtx.prependMatrix(matrix); } - - var l = this.children.length, rect=null; - for (var i=0; i ColorMatrix.LENGTH) { + matrix = matrix.slice(0,ColorMatrix.LENGTH); } - return rect; + return matrix; }; - createjs.Container = createjs.promote(Container, "DisplayObject"); -}()); - -//############################################################################## -// Stage.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * A stage is the root level {{#crossLink "Container"}}{{/crossLink}} for a display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}} - * method is called, it will render its display list to its target canvas. - * - *

      Example

      - * This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child - * and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}. - * - * var stage = new createjs.Stage("canvasElementId"); - * var image = new createjs.Bitmap("imagePath.png"); - * stage.addChild(image); - * createjs.Ticker.addEventListener("tick", handleTick); - * function handleTick(event) { - * image.x += 10; - * stage.update(); - * } - * - * @class Stage - * @extends Container - * @constructor - * @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id - * of a canvas object in the current document. - **/ - function Stage(canvas) { - this.Container_constructor(); - - - // public properties: - /** - * Indicates whether the stage should automatically clear the canvas before each render. You can set this to false - * to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for - * example). - * - *

      Example

      - * - * var stage = new createjs.Stage("canvasId"); - * stage.autoClear = false; - * - * @property autoClear - * @type Boolean - * @default true - **/ - this.autoClear = true; - - /** - * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the - * first stage that will be ticked (or they will clear each other's render). - * - * When changing the canvas property you must disable the events on the old canvas, and enable events on the - * new canvas or mouse events will not work as expected. For example: - * - * myStage.enableDOMEvents(false); - * myStage.canvas = anotherCanvas; - * myStage.enableDOMEvents(true); - * - * @property canvas - * @type HTMLCanvasElement | Object - **/ - this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas; - - /** - * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent - * position over the canvas, and mouseInBounds will be set to false. - * @property mouseX - * @type Number - * @readonly - **/ - this.mouseX = 0; - - /** - * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent - * position over the canvas, and mouseInBounds will be set to false. - * @property mouseY - * @type Number - * @readonly - **/ - this.mouseY = 0; - - /** - * Specifies the area of the stage to affect when calling update. This can be use to selectively - * re-draw specific regions of the canvas. If null, the whole canvas area is drawn. - * @property drawRect - * @type {Rectangle} - */ - this.drawRect = null; - - /** - * Indicates whether display objects should be rendered on whole pixels. You can set the - * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of - * display objects to false to enable/disable this behaviour on a per instance basis. - * @property snapToPixelEnabled - * @type Boolean - * @default false - **/ - this.snapToPixelEnabled = false; - - /** - * Indicates whether the mouse is currently within the bounds of the canvas. - * @property mouseInBounds - * @type Boolean - * @default false - **/ - this.mouseInBounds = false; - - /** - * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas. - * @property tickOnUpdate - * @type Boolean - * @default true - **/ - this.tickOnUpdate = true; - - /** - * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See - * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}} - * x/y/rawX/rawY. - * @property mouseMoveOutside - * @type Boolean - * @default false - **/ - this.mouseMoveOutside = false; - - - /** - * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas. - * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas. - * @property preventSelection - * @type Boolean - * @default true - **/ - this.preventSelection = true; - - /** - * The hitArea property is not supported for Stage. - * @property hitArea - * @type {DisplayObject} - * @default null - */ - - - // private properties: - /** - * Holds objects with data for each active pointer id. Each object has the following properties: - * x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position) - * @property _pointerData - * @type {Object} - * @private - */ - this._pointerData = {}; - - /** - * Number of active pointers. - * @property _pointerCount - * @type {Object} - * @private - */ - this._pointerCount = 0; - - /** - * The ID of the primary pointer. - * @property _primaryPointerID - * @type {Object} - * @private - */ - this._primaryPointerID = null; - - /** - * @property _mouseOverIntervalID - * @protected - * @type Number - **/ - this._mouseOverIntervalID = null; - - /** - * @property _nextStage - * @protected - * @type Stage - **/ - this._nextStage = null; - - /** - * @property _prevStage - * @protected - * @type Stage - **/ - this._prevStage = null; - - - // initialize: - this.enableDOMEvents(true); - } - var p = createjs.extend(Stage, createjs.Container); - -// events: - /** - * Dispatched when the user moves the mouse over the canvas. - * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event stagemousemove - * @since 0.6.0 - */ - - /** - * Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}} - * class for a listing of event properties. - * @event stagemousedown - * @since 0.6.0 - */ - - /** - * Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers). - * You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds. - * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties. - * @event stagemouseup - * @since 0.6.0 - */ - - /** - * Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false). - * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}} - * class for a listing of event properties. - * @event mouseleave - * @since 0.7.0 - */ - - /** - * Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true). - * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}} - * class for a listing of event properties. - * @event mouseenter - * @since 0.7.0 - */ - - /** - * Dispatched each update immediately before the tick event is propagated through the display list. - * You can call preventDefault on the event object to cancel propagating the tick event. - * @event tickstart - * @since 0.7.0 - */ - - /** - * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if - * tickOnUpdate is false. Precedes the "drawstart" event. - * @event tickend - * @since 0.7.0 - */ - - /** - * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. - * You can call preventDefault on the event object to cancel the draw. - * @event drawstart - * @since 0.7.0 - */ - - /** - * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored. - * @event drawend - * @since 0.7.0 - */ - - -// getter / setters: - /** - * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them. - * This can be useful in cases where you have multiple layered canvases and want user interactions - * events to pass through. For example, this would relay mouse events from topStage to bottomStage: - * - * topStage.nextStage = bottomStage; - * - * To disable relaying, set nextStage to null. - * - * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings - * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled. - * Considerations when using roll over in relay targets:
        - *
      1. The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)
      2. - *
      3. All stages that wish to participate in mouse over interaction must enable them via enableMouseOver
      4. - *
      5. All relay targets will share the frequency value of the top-most stage
      6. - *
      - * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing - * 30 as it's desired frequency): - * topStage.nextStage = targetStage; - * topStage.enableMouseOver(10); - * targetStage.enableMouseOver(30); - * - * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its - * DOM events using: - * - * targetStage.enableDOMEvents(false); - * - * @property nextStage - * @type {Stage} - **/ - p._get_nextStage = function() { - return this._nextStage; - }; - p._set_nextStage = function(value) { - if (this._nextStage) { this._nextStage._prevStage = null; } - if (value) { value._prevStage = this; } - this._nextStage = value; - }; - - try { - Object.defineProperties(p, { - nextStage: { get: p._get_nextStage, set: p._set_nextStage } - }); - } catch (e) {} // TODO: use Log - - -// public methods: - /** - * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}} - * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false, - * and then render the display list to the canvas. - * - * @method update - * @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property. - **/ - p.update = function(props) { - if (!this.canvas) { return; } - if (this.tickOnUpdate) { this.tick(props); } - if (this.dispatchEvent("drawstart", false, true) === false) { return; } - createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled; - var r = this.drawRect, ctx = this.canvas.getContext("2d"); - ctx.setTransform(1, 0, 0, 1, 0, 0); - if (this.autoClear) { - if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); } - else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); } - } - ctx.save(); - if (this.drawRect) { - ctx.beginPath(); - ctx.rect(r.x, r.y, r.width, r.height); - ctx.clip(); - } - this.updateContext(ctx); - this.draw(ctx, false); - ctx.restore(); - this.dispatchEvent("drawend"); - }; - - /** - * 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. - * - * If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is - * propagated to listeners. - * - * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that - * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be - * passed as the `props` parameter to `tick()`. For example: - * - * Ticker.on("tick", handleTick); - * function handleTick(evtObj) { - * // clone the event object from Ticker, and add some custom data to it: - * var evt = evtObj.clone().set({greeting:"hello", name:"world"}); - * - * // pass it to stage.update(): - * myStage.update(evt); // subsequently calls tick() with the same param - * } - * - * // ... - * myDisplayObject.on("tick", handleDisplayObjectTick); - * function handleDisplayObjectTick(evt) { - * console.log(evt.delta); // the delta property from the Ticker tick event object - * console.log(evt.greeting, evt.name); // custom data: "hello world" - * } - * - * @method tick - * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property. - **/ - p.tick = function(props) { - if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; } - var evtObj = new createjs.Event("tick"); - if (props) { - for (var n in props) { - if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; } - } - } - this._tick(evtObj); - this.dispatchEvent("tickend"); - }; - - /** - * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}} - * event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}} - * directly, using: - * - * Ticker.addEventListener("tick", myStage); - * - * Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to - * display object tick handlers, instead of delta and paused parameters. - * @property handleEvent - * @type Function - **/ - p.handleEvent = function(evt) { - if (evt.type == "tick") { this.update(evt); } - }; - - /** - * Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`. - * @method clear - **/ - p.clear = function() { - if (!this.canvas) { return; } - var ctx = this.canvas.getContext("2d"); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); - }; - - /** - * Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can - * be specified as the src value of an image element. - * @method toDataURL - * @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color - * value is allowed. The default value is a transparent background. - * @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type - * is passed in, or if the browser does not support the specified MIME type, the default value will be used. - * @return {String} a Base64 encoded image. - **/ - p.toDataURL = function(backgroundColor, mimeType) { - var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height; - - if (backgroundColor) { - data = ctx.getImageData(0, 0, w, h); - var compositeOperation = ctx.globalCompositeOperation; - ctx.globalCompositeOperation = "destination-over"; - - ctx.fillStyle = backgroundColor; - ctx.fillRect(0, 0, w, h); - } - - var dataURL = this.canvas.toDataURL(mimeType||"image/png"); - - if(backgroundColor) { - ctx.putImageData(data, 0, 0); - ctx.globalCompositeOperation = compositeOperation; - } - - return dataURL; - }; - - /** - * Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}} - * and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}} - * and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can - * be expensive to generate, so they are disabled by default. The frequency of the events can be controlled - * independently of mouse move events via the optional `frequency` parameter. - * - *

      Example

      - * - * var stage = new createjs.Stage("canvasId"); - * stage.enableMouseOver(10); // 10 updates per second - * - * @method enableMouseOver - * @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast - * mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less - * responsive, but uses less CPU. - **/ - p.enableMouseOver = function(frequency) { - if (this._mouseOverIntervalID) { - clearInterval(this._mouseOverIntervalID); - this._mouseOverIntervalID = null; - if (frequency == 0) { - this._testMouseOver(true); - } - } - if (frequency == null) { frequency = 20; } - else if (frequency <= 0) { return; } - var o = this; - this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency)); - }; - - /** - * Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good - * practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive - * events from the page. - * - * When changing the canvas property you must disable the events on the old canvas, and enable events on the - * new canvas or mouse events will not work as expected. For example: - * - * myStage.enableDOMEvents(false); - * myStage.canvas = anotherCanvas; - * myStage.enableDOMEvents(true); - * - * @method enableDOMEvents - * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true. - **/ - p.enableDOMEvents = function(enable) { - if (enable == null) { enable = true; } - var n, o, ls = this._eventListeners; - if (!enable && ls) { - for (n in ls) { - o = ls[n]; - o.t.removeEventListener(n, o.f, false); - } - this._eventListeners = null; - } else if (enable && !ls && this.canvas) { - var t = window.addEventListener ? window : document; - var _this = this; - ls = this._eventListeners = {}; - ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} }; - ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} }; - ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} }; - ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} }; - - for (n in ls) { - o = ls[n]; - o.t.addEventListener(n, o.f, false); - } - } - }; - - /** - * Stage instances cannot be cloned. - * @method clone - **/ - p.clone = function() { - throw("Stage cannot be cloned."); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Stage (name="+ this.name +")]"; - }; - - -// private methods: - /** - * @method _getElementRect - * @protected - * @param {HTMLElement} e - **/ - p._getElementRect = function(e) { - var bounds; - try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9 - catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; } - - var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0); - var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0); - - var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility. - var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth); - var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth); - var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth); - var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth); - - // note: in some browsers bounds properties are read only. - return { - left: bounds.left+offX+padL, - right: bounds.right+offX-padR, - top: bounds.top+offY+padT, - bottom: bounds.bottom+offY-padB - } - }; - - /** - * @method _getPointerData - * @protected - * @param {Number} id - **/ - p._getPointerData = function(id) { - var data = this._pointerData[id]; - if (!data) { data = this._pointerData[id] = {x:0,y:0}; } - return data; - }; - - /** - * @method _handleMouseMove - * @protected - * @param {MouseEvent} e - **/ - p._handleMouseMove = function(e) { - if(!e){ e = window.event; } - this._handlePointerMove(-1, e, e.pageX, e.pageY); - }; - - /** - * @method _handlePointerMove - * @protected - * @param {Number} id - * @param {Event} e - * @param {Number} pageX - * @param {Number} pageY - * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. - **/ - p._handlePointerMove = function(id, e, pageX, pageY, owner) { - if (this._prevStage && owner === undefined) { return; } // redundant listener. - if (!this.canvas) { return; } - var nextStage=this._nextStage, o=this._getPointerData(id); - - var inBounds = o.inBounds; - this._updatePointerPosition(id, e, pageX, pageY); - if (inBounds || o.inBounds || this.mouseMoveOutside) { - if (id === -1 && o.inBounds == !inBounds) { - this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e); - } - - this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e); - this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e); - } - - nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null); - }; - - /** - * @method _updatePointerPosition - * @protected - * @param {Number} id - * @param {Event} e - * @param {Number} pageX - * @param {Number} pageY - **/ - p._updatePointerPosition = function(id, e, pageX, pageY) { - var rect = this._getElementRect(this.canvas); - pageX -= rect.left; - pageY -= rect.top; - - var w = this.canvas.width; - var h = this.canvas.height; - pageX /= (rect.right-rect.left)/w; - pageY /= (rect.bottom-rect.top)/h; - var o = this._getPointerData(id); - if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) { - o.x = pageX; - o.y = pageY; - } else if (this.mouseMoveOutside) { - o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX); - o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY); - } - - o.posEvtObj = e; - o.rawX = pageX; - o.rawY = pageY; - - if (id === this._primaryPointerID || id === -1) { - this.mouseX = o.x; - this.mouseY = o.y; - this.mouseInBounds = o.inBounds; - } - }; - - /** - * @method _handleMouseUp - * @protected - * @param {MouseEvent} e - **/ - p._handleMouseUp = function(e) { - this._handlePointerUp(-1, e, false); - }; - - /** - * @method _handlePointerUp - * @protected - * @param {Number} id - * @param {Event} e - * @param {Boolean} clear - * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. - **/ - p._handlePointerUp = function(id, e, clear, owner) { - var nextStage = this._nextStage, o = this._getPointerData(id); - if (this._prevStage && owner === undefined) { return; } // redundant listener. - - var target=null, oTarget = o.target; - if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); } - - if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; } - - if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); } - this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e); - - if (clear) { - if (id==this._primaryPointerID) { this._primaryPointerID = null; } - delete(this._pointerData[id]); - } else { o.target = null; } - - nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this); - }; - - /** - * @method _handleMouseDown - * @protected - * @param {MouseEvent} e - **/ - p._handleMouseDown = function(e) { - this._handlePointerDown(-1, e, e.pageX, e.pageY); - }; - - /** - * @method _handlePointerDown - * @protected - * @param {Number} id - * @param {Event} e - * @param {Number} pageX - * @param {Number} pageY - * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. - **/ - p._handlePointerDown = function(id, e, pageX, pageY, owner) { - if (this.preventSelection) { e.preventDefault(); } - if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over. - - if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); } - var target = null, nextStage = this._nextStage, o = this._getPointerData(id); - if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); } - - if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; } - this._dispatchMouseEvent(target, "mousedown", true, id, o, e); - - nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this); - }; - - /** - * @method _testMouseOver - * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target) - * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. - * @param {Stage} eventTarget The stage that the cursor is actively over. - * @protected - **/ - p._testMouseOver = function(clear, owner, eventTarget) { - if (this._prevStage && owner === undefined) { return; } // redundant listener. - - var nextStage = this._nextStage; - if (!this._mouseOverIntervalID) { - // not enabled for mouseover, but should still relay the event. - nextStage&&nextStage._testMouseOver(clear, owner, eventTarget); - return; - } - var o = this._getPointerData(-1); - // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs. - if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; } - - var e = o.posEvtObj; - var isEventTarget = eventTarget || e&&(e.target == this.canvas); - var target=null, common = -1, cursor="", t, i, l; - - if (!owner && (clear || this.mouseInBounds && isEventTarget)) { - target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true); - this._mouseOverX = this.mouseX; - this._mouseOverY = this.mouseY; - } - - var oldList = this._mouseOverTarget||[]; - var oldTarget = oldList[oldList.length-1]; - var list = this._mouseOverTarget = []; - - // generate ancestor list and check for cursor: - t = target; - while (t) { - list.unshift(t); - if (!cursor) { cursor = t.cursor; } - t = t.parent; - } - this.canvas.style.cursor = cursor; - if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; } - - // find common ancestor: - for (i=0,l=list.length; icommon; i--) { - this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target); - } - - for (i=list.length-1; i>common; i--) { - this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget); - } - - if (oldTarget != target) { - this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget); - } - - nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this); - }; - - /** - * @method _handleDoubleClick - * @protected - * @param {MouseEvent} e - * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. - **/ - p._handleDoubleClick = function(e, owner) { - var target=null, nextStage=this._nextStage, o=this._getPointerData(-1); - if (!owner) { - target = this._getObjectsUnderPoint(o.x, o.y, null, true); - this._dispatchMouseEvent(target, "dblclick", true, -1, o, e); - } - nextStage&&nextStage._handleDoubleClick(e, owner || target && this); - }; - - /** - * @method _dispatchMouseEvent - * @protected - * @param {DisplayObject} target - * @param {String} type - * @param {Boolean} bubbles - * @param {Number} pointerId - * @param {Object} o - * @param {MouseEvent} [nativeEvent] - * @param {DisplayObject} [relatedTarget] - **/ - p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) { - // TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC. - if (!target || (!bubbles && !target.hasEventListener(type))) { return; } - /* - // TODO: account for stage transformations? - this._mtx = this.getConcatenatedMatrix(this._mtx).invert(); - var pt = this._mtx.transformPoint(o.x, o.y); - var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY); - */ - var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget); - target.dispatchEvent(evt); - }; - - - createjs.Stage = createjs.promote(Stage, "Container"); -}()); - -//############################################################################## -// StageGL.js -//############################################################################## - -this.createjs = this.createjs||{}; - -/* - * README IF EDITING: - * 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 - * 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. - */ - -(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. - * - * 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 - * support WebGL, content will automatically be rendered to canvas 2D context instead. - * - *

      Limitations

      - * - {{#crossLink "Shape"}}{{/crossLink}}, {{#crossLink "Shadow"}}{{/crossLink}}, and {{#crossLink "Text"}}{{/crossLink}} - * are not rendered when added to the display list. - * - To display something StageGL cannot render, {{#crossLink "displayObject/cache"}}{{/crossLink}} the object. - * Caches can be rendered regardless of source. - * - Images are wrapped as a webGL "Texture". Each graphics card has a limit to its concurrent Textures, too many - * Textures will noticeably slow performance. - * - Each cache counts as an individual Texture. As such {{#crossLink "SpriteSheet"}}{{/crossLink}} and - * {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} are recommended practices to help keep texture counts low. - * - To use any image node (DOM Image/Canvas Element) between multiple StageGL instances it must be a - * {{#crossLink "Bitmap/clone"}}{{/crossLink}}, otherwise the GPU texture loading and tracking will get confused. - * - to avoid an up/down scaled render you must call {{#crossLink "StageGL/updateViewport"}}{{/crossLink}} if you - * 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. - * - *

      Example

      - * This example creates a StageGL instance, adds a child to it, then uses the EaselJS {{#crossLink "Ticker"}}{{/crossLink}} - * to update the child and redraw the stage. - * - * var stage = new createjs.StageGL("canvasElementId"); - * - * var image = new createjs.Bitmap("imagePath.png"); - * stage.addChild(image); - * - * createjs.Ticker.on("tick", handleTick); - * - * function handleTick(event) { - * image.x += 10; - * 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. - * @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. - * @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). - * @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. - */ - function StageGL(canvas, options) { - this.Stage_constructor(canvas); - - 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; - } - -// public properties: - /** - * Console log potential issues and problems. This is designed to have minimal performance impact, so - * if extensive debugging information is required, this may be inadequate. See {{#crossLink "WebGLInspector"}}{{/crossLink}} - * @property vocalDebug - * @type {Boolean} - * @default false - */ - this.vocalDebug = false; - -// private properties: - /** - * Specifies whether or not the canvas is auto-cleared by WebGL. The WebGL spec discourages `true`. - * If true, the canvas is NOT auto-cleared by WebGL. Used when the canvas context is created and requires - * context re-creation to update. - * @property _preserveBuffer - * @protected - * @type {Boolean} - * @default false - */ - this._preserveBuffer = preserveBuffer||false; - - /** - * Specifies whether or not the browser's WebGL implementation should try to perform anti-aliasing. - * @property _antialias - * @protected - * @type {Boolean} - * @default false - */ - this._antialias = antialias||false; - - /** - * Specifies whether or not the browser's WebGL implementation should be transparent. - * @property _transparent - * @protected - * @type {Boolean} - * @default false - */ - 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} - * @default null - */ - this._autoPurge = undefined; - this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating - - /** - * The width in px of the drawing surface saved in memory. - * @property _viewportWidth - * @protected - * @type {Number} - * @default 0 - */ - this._viewportWidth = 0; - - /** - * The height in px of the drawing surface saved in memory. - * @property _viewportHeight - * @protected - * @type {Number} - * @default 0 - */ - this._viewportHeight = 0; - - /** - * A 2D projection matrix used to convert WebGL's viewspace into canvas co-ordinates. Regular canvas display - * uses Top-Left values of [0,0] where WebGL uses a Center [0,0] Top-Right [1,1] (euclidean) system. - * @property _projectionMatrix - * @protected - * @type {Float32Array} - * @default null - */ - this._projectionMatrix = null; - - /** - * The current WebGL canvas context. Often shorthanded to just "gl" in many parts of the code. - * @property _webGLContext - * @protected - * @type {WebGLRenderingContext} - * @default null - */ - this._webGLContext = null; - - /** - * The color to use when the WebGL canvas has been cleared. May appear as a background color. Defaults to grey. - * @property _clearColor - * @protected - * @type {Object} - * @default {r: 0.50, g: 0.50, b: 0.50, a: 0.00} - */ - this._clearColor = {r: 0.50, g: 0.50, b: 0.50, a: 0.00}; - - /** - * 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 - * @protected - * @type {Number} - * @default StageGL.DEFAULT_MAX_BATCH_SIZE (10000) - */ - this._maxCardsPerBatch = StageGL.DEFAULT_MAX_BATCH_SIZE; //TODO: write getter/setters for this - - /** - * The shader program used to draw the current batch. - * @property _activeShader - * @protected - * @type {WebGLProgram} - * @default null - */ - this._activeShader = null; - - /** - * The vertex position data for the current draw call. - * @property _vertices - * @protected - * @type {Float32Array} - * @default null - */ - this._vertices = null; - - /** - * The WebGL buffer attached to {{#crossLink "StageGL/_vertices:property"}}{{/crossLink}}. - * @property _vertexPositionBuffer - * @protected - * @type {WebGLBuffer} - * @default null - */ - this._vertexPositionBuffer = null; - - /** - * The vertex U/V data for the current draw call. - * @property _uvs - * @protected - * @type {Float32Array} - * @default null - */ - this._uvs = null; - - /** - * The WebGL buffer attached to {{#crossLink "StageGL/_uvs:property"}}{{/crossLink}}. - * @property _uvPositionBuffer - * @protected - * @type {WebGLBuffer} - * @default null - */ - this._uvPositionBuffer = null; - - /** - * The vertex indices data for the current draw call. - * @property _indices - * @protected - * @type {Float32Array} - * @default null - */ - this._indices = null; - - /** - * The WebGL buffer attached to {{#crossLink "StageGL/_indices:property"}}{{/crossLink}}. - * @property _textureIndexBuffer - * @protected - * @type {WebGLBuffer} - * @default null - */ - this._textureIndexBuffer = null; - - /** - * The vertices data for the current draw call. - * @property _alphas - * @protected - * @type {Float32Array} - * @default null - */ - this._alphas = null; - - /** - * The WebGL buffer attached to {{#crossLink "StageGL/_alphas:property"}}{{/crossLink}}. - * @property _alphaBuffer - * @protected - * @type {WebGLBuffer} - * @default null - */ - this._alphaBuffer = null; - - /** - * An index based lookup of every WebGL Texture currently in use. - * @property _drawTexture - * @protected - * @type {Array} - */ - this._textureDictionary = []; - - /** - * A string based lookup hash of which index a texture is stored at in the dictionary. The lookup string is - * often the src url. - * @property _textureIDs - * @protected - * @type {Object} - */ - this._textureIDs = {}; - - /** - * An array of all the textures currently loaded into the GPU. The index in the array matches the GPU index. - * @property _batchTextures - * @protected - * @type {Array} - */ - this._batchTextures = []; - - /** - * An array of all the simple filler textures used to prevent issues with missing textures in a batch. - * @property _baseTextures - * @protected - * @type {Array} - */ - 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 - * @protected - * @type {Number} - * @default 8 - */ - this._batchTextureCount = 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 - * @protected - * @type {Number} - * @default -1 - */ - this._lastTextureInsert = -1; - - /** - * The current batch being drawn, A batch consists of a call to `drawElements` on the GPU. Many of these calls - * can occur per draw. - * @property _batchId - * @protected - * @type {Number} - * @default 0 - */ - this._batchID = 0; - - /** - * The current draw being performed, may contain multiple batches. Comparing to {{#crossLink "StageGL/_batchID:property"}}{{/crossLink}} - * can reveal batching efficiency. - * @property _drawID - * @protected - * @type {Number} - * @default 0 - */ - this._drawID = 0; - - /** - * Used to prevent textures in certain GPU slots from being replaced by an insert. - * @property _slotBlackList - * @protected - * @type {Array} - */ - 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 - * @protected - * @type {Number} - * @default 0 - */ - this._lastTrackedCanvas = 0; - - /** - * 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. - * @property _cacheContainer - * @protected - * @type {Container} - * @default An instance of an EaselJS Container. - */ - this._cacheContainer = new createjs.Container(); - - // and begin - this._initializeWebGL(); - } - 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 - * the ability to get info back for a specific frame, or only calculate that one frame. - * - * //generate UV rects for all entries - * StageGL.buildUVRects( spriteSheetA ); - * //generate all, fetch the first - * var firstFrame = StageGL.buildUVRects( spriteSheetB, 0 ); - * //generate the rect for just a single frame for performance's sake - * var newFrame = StageGL.buildUVRects( dynamicSpriteSheet, newFrameIndex, true ); - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method buildUVRects - * @param {SpriteSheet} spritesheet The spritesheet to find the frames on - * @param {int} [target=-1] The index of the frame to return - * @param {Boolean} [onlyTarget=false] Whether "target" is the only frame that gets calculated - * @static - * @return {Object} the target frame if supplied and present or a generic frame {t, l, b, r} - */ - StageGL.buildUVRects = function (spritesheet, target, onlyTarget) { - if (!spritesheet || !spritesheet._frames) { return null; } - 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); - 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 - * @static - * @final - * @type {String} - * @readonly - */ - StageGL.COVER_VARYING_HEADER = ( - "precision mediump float;" + - - "varying highp vec2 vRenderCoord;" + - "varying highp vec2 vTextureCoord;" - ); - - /** - * Actual full header for the vertex shader. Includes the varying header. 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_HEADER - * @static - * @final - * @type {String} - * @readonly - */ - StageGL.COVER_VERTEX_HEADER = ( - StageGL.COVER_VARYING_HEADER + - "attribute vec2 vertexPosition;" + - "attribute vec2 uvPosition;" + - "uniform float uUpright;" - ); - - /** - * Actual full header for the fragment shader. Includes the varying header. 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_HEADER - * @static - * @final - * @type {String} - * @readonly - */ - StageGL.COVER_FRAGMENT_HEADER = ( - StageGL.COVER_VARYING_HEADER + - "uniform sampler2D uSampler;" - ); - - /** - * 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 - * @static - * @final - * @type {String} - * @readonly - */ - 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));" + - "}" - ); - - /** - * 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 - * @static - * @final - * @type {String} - * @readonly - */ - StageGL.COVER_FRAGMENT_BODY = ( - "void main(void) {" + - "vec4 color = texture2D(uSampler, vRenderCoord);" + - "gl_FragColor = color;" + - "}" - ); - -// events: - /** - * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. You can call - * {{#crossLink "Event/preventDefault"}}{{/crossLink}} on the event to cancel the draw. - * @event drawstart - */ - - /** - * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored. - * @event drawend - */ - -// getter / setters: - p._get_isWebGL = function () { - return !!this._webGLContext; - }; - - p._set_autoPurge = function (value) { - value = isNaN(value)?1200:value; - if (value != -1) { - value = value<10?10:value; - } - this._autoPurge = value; - }; - p._get_autoPurge = function () { - return Number(this._autoPurge); - }; - - try { - Object.defineProperties(p, { - /** - * Indicates whether WebGL is being used for rendering. For example, this would be `false` if WebGL is not - * supported in the browser. - * @property isWebGL - * @type {Boolean} - * @readonly - */ - isWebGL: { get: p._get_isWebGL }, - - /** - * 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. - * @property autoPurge - * @protected - * @type {Integer} - * @default 1000 - */ - autoPurge: { get: p._get_autoPurge, set: p._set_autoPurge } - }); - } catch (e) {} // TODO: use Log - - -// constructor methods: - /** - * Create and properly initialize the WebGL instance. - * @method _initializeWebGL - * @protected - * @return {WebGLRenderingContext} - */ - p._initializeWebGL = function () { - if (this.canvas) { - if (!this._webGLContext || this._webGLContext.canvas !== this.canvas) { - // A context hasn't been defined yet, - // OR the defined context belongs to a different canvas, so reinitialize. - - // 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, - 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.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); - - 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); - } - } else { - this._webGLContext = null; - } - return this._webGLContext; - }; - -// public methods: - /** - * Docced in superclass - */ - p.update = function (props) { - 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); - } - } else { - // Use 2D. - var ctx = this.canvas.getContext("2d"); - ctx.save(); - this.updateContext(ctx); - this.draw(ctx, false); - ctx.restore(); - } - this.dispatchEvent("drawend"); - }; - - /** - * Docced in superclass - */ - 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. - this.Stage_clear(); - } - }; - - /** - * Draws the stage into the supplied context if possible. Many WebGL properties only exist on their context. As such - * you cannot share contexts among many StageGLs and each context requires a unique StageGL instance. Contexts that - * don't match the context managed by this StageGL will be treated as a 2D context. - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method draw - * @param {CanvasRenderingContext2D | WebGLRenderingContext} context The 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). - * @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 { - return this.Stage_draw(context, ignoreCache); - } - }; - - /** - * Draws the target into the correct context, be it a canvas or Render Texture using WebGL. - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @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 { - 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; - }; - - /** - * 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; - } - if (!result) { - throw "Problems creating render textures, known causes include using too much VRAM by not releasing WebGL texture instances"; - } - target.__lastRT = result; - return result; - }; - - /** - * For every image encountered StageGL registers and tracks it automatically. This tracking can cause memory leaks - * if not purged. StageGL, by default, automatically purges them. This does have a cost and may unfortunately find - * false positives. This function is for manual management of this memory instead of the automatic system controlled - * by the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property. - * - * This function will recursively remove all textures found on the object, its children, cache, etc. It will uncache - * objects and remove any texture it finds REGARDLESS of whether it is currently in use elsewhere. It is up to the - * 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. - * @method releaseTexture - * @param {DisplayObject | Texture | Image | Canvas} item An object that used the texture to be discarded. - */ - p.releaseTexture = function (item) { - var i, l; - if (!item) { return; } - - // this is a container object - if (item.children) { - for (i = 0, l = item.children.length; i < l; i++) { - this.releaseTexture(item.children[i]); - } - } - - // this has a cache canvas - if (item.cacheCanvas) { - item.uncache(); - } - - var foundImage = undefined; - if (item._storeID !== undefined) { - // this is a texture itself - if (item === this._textureDictionary[item._storeID]) { - this._killTextureObject(item); - item._storeID = undefined; - return; - } - - // this is an image or canvas - foundImage = item; - } else if (item._webGLRenderStyle === 2) { - // this is a Bitmap class - foundImage = item.image; - } 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]); - } - return; - } - - // did we find anything - if (foundImage === undefined) { - if (this.vocalDebug) { - console.log("No associated texture found on release"); - } - return; - } - - // remove it - this._killTextureObject(this._textureDictionary[foundImage._storeID]); - foundImage._storeID = undefined; - }; - - /** - * Similar to {{#crossLink "releaseTexture"}}{{/crossLink}}, but this function differs by searching for textures to - * release. It works by assuming that it can purge any texture which was last used more than "count" draw calls ago. - * Because this process is unaware of the objects and whether they may be used on your stage, false positives can - * occur. It is recommended to manually manage your memory with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}}, - * however, there are many use cases where this is simpler and error-free. This process is also run by default under - * the hood to prevent leaks. To disable it see the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property. - * @method purgeTextures - * @param {Number} [count=100] How many renders ago the texture was last used - */ - p.purgeTextures = function (count) { - if (count == undefined){ count = 100; } - - var dict = this._textureDictionary; - var l = dict.length; - for (var i= 0; inot 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. - */ - p.updateViewport = function (width, height) { - this._viewportWidth = width|0; - this._viewportHeight = height|0; - var gl = this._webGLContext; - - if (gl) { - gl.viewport(0, 0, this._viewportWidth, this._viewportHeight); - - // WebGL works with a -1,1 space on its screen. It also follows Y-Up - // we need to flip the y, scale and then translate the co-ordinates to match this - // 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, 0, 1, 0, - -1, 1, 0.1, 0 - ]); - // 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; - } - }; - - /** - * Fetches the shader compiled and set up to work with the provided filter/object. The shader is compiled on first - * use and returned on subsequent calls. - * @method getFilterShader - * @param {Filter|Object} filter The object which will provide the information needed to construct the filter shader. - * @return {WebGLProgram} - */ - p.getFilterShader = function (filter) { - if (!filter) { filter = this; } - - var gl = this._webGLContext; - var targetShader = this._activeShader; - - if (filter._builtShader) { - targetShader = filter._builtShader; - if (filter.shaderParamSetup) { - gl.useProgram(targetShader); - filter.shaderParamSetup(gl, this, targetShader); - } - } else { - try { - targetShader = this._fetchShaderProgram( - gl, "filter", - filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY, - filter.shaderParamSetup && filter.shaderParamSetup.bind(filter) - ); - filter._builtShader = targetShader; - targetShader._name = filter.toString(); - } catch (e) { - console && console.log("SHADER SWITCH FAILURE", e); - } - } - return targetShader; - }; - - /** - * Returns a base texture that has no image or data loaded. Not intended for loading images. It may return `null` - * in some error cases, and trying to use a "null" texture can cause renders to fail. - * @method getBaseTexture - * @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 - */ - p.getBaseTexture = function (w, h) { - var width = Math.ceil(w > 0 ? w : 1) || 1; - var height = Math.ceil(h > 0 ? h : 1) || 1; - - var gl = this._webGLContext; - var texture = gl.createTexture(); - this.resizeTexture(texture, width, height); - this.setTextureParams(gl, false); - - return texture; - }; - - /** - * Resizes a supplied texture element. May generate invalid textures in some error cases such as; when the texture - * is too large, when an out of texture memory error occurs, or other error scenarios. Trying to use an invalid texture - * can cause renders to hard stop on some devices. Check the WebGL bound texture after running this function. - * - * NOTE: The supplied texture must have been made with the WebGL "texImage2D" function, all default APIs in StageGL - * use this, so this note only matters for library developers and plugins. - * - * @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 - */ - p.resizeTexture = function (texture, width,height) { - var gl = this._webGLContext; - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texImage2D( - gl.TEXTURE_2D, // target - 0, // level of detail - gl.RGBA, // internal format - width, height, 0, // width, height, border (only for array/null sourced textures) - gl.RGBA, // format (match internal format) - gl.UNSIGNED_BYTE, // type of texture(pixel color depth) - null // image data, we can do null because we're doing array data - ); - texture.width = width; - texture.height = height; - }; - - /** - * 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. - * @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. - */ - 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 - renderTexture.width = w; - renderTexture.height = h; - - // attach frame buffer to texture and provide cross links to look up each other - gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture, 0); - frameBuffer._renderTexture = renderTexture; - renderTexture._frameBuffer = frameBuffer; - - // these keep track of themselves simply to reduce complexity of some lookup code - renderTexture._storeID = this._textureDictionary.length; - this._textureDictionary[renderTexture._storeID] = renderTexture; - - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - return renderTexture; - }; - - /** - * Common utility function used to apply the correct texture processing parameters for the bound texture. - * @method setTextureParams - * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. - * @param {Boolean} [isPOT=false] Marks whether the texture is "Power of Two", this may allow better quality AA. - */ - p.setTextureParams = function (gl, isPOT) { - if (isPOT && this._antialias) { - //non POT linear works in some devices, but performance is NOT good, investigate - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } else { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - }; - - /** - * Changes the webGL clear, aka "background" color to the provided value. A transparent clear is recommended, as - * non-transparent colours may create undesired boxes around some visuals. - * - * The clear color will also be used for filters and other "render textures". The stage background will ignore the - * transparency value and display a solid color normally. For the stage to recognize and use transparency it must be - * created with the transparent flag set to `true` (see {{#crossLink "StageGL/constructor"}}{{/crossLink}})). - * - * Using "transparent white" to demonstrate, the valid data formats are as follows: - *
        - *
      • "#FFF"
      • - *
      • "#FFFFFF"
      • - *
      • "#FFFFFF00"
      • - *
      • "rgba(255,255,255,0.0)"
      • - *
      - * @method setClearColor - * @param {String|int} [color=0x00000000] The new color to use as the background - */ - p.setClearColor = function (color) { - var r, g, b, a, output; - - 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) { - output = color.slice(5, -1).split(","); - r = Number(output[0])/255; - g = Number(output[1])/255; - b = Number(output[2])/255; - a = Number(output[3]); - } - } else { // >>> is an unsigned shift which is what we want as 0x80000000 and up are negative values - r = ((color & 0xFF000000) >>> 24)/255; - g = ((color & 0x00FF0000) >>> 16)/255; - b = ((color & 0x0000FF00) >>> 8)/255; - a = (color & 0x000000FF)/255; - } - - this._clearColor.r = r || 0; - 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); - }; - - /** - * docced in subclass - */ - p.toString = function () { - return "[StageGL (name="+ this.name +")]"; - }; - -// private methods: - /** - * 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. - * @param {Canvas} canvas The DOM canvas element to attach to - * @param {Object} options The options to be handed into the WebGL object, see WebGL spec - * @method _fetchWebGLContext - * @protected - * @return {WebGLRenderingContext} The WebGL context, may return undefined in error scenarios - */ - p._fetchWebGLContext = function (canvas, options) { - var gl; - - try { - gl = canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options); - } catch (e) { - // don't do anything in catch, null check will handle it - } - - if (!gl) { - var msg = "Could not initialize WebGL"; - console.error?console.error(msg):console.log(msg); - } else { - gl.viewportWidth = canvas.width; - gl.viewportHeight = canvas.height; - } - - return gl; - }; - - /** - * Create the completed Shader Program from the vertex and fragment shaders. Allows building of custom shaders for - * 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. - * 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 - * {{#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 - * {{#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. - * @protected - * @return {WebGLProgram} The compiled and linked shader - */ - p._fetchShaderProgram = function (gl, shaderName, customVTX, customFRAG, shaderParamSetup) { - 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; - } - - // create the separate vars - var vertexShader = this._createShader(gl, gl.VERTEX_SHADER, targetVtx); - var fragmentShader = this._createShader(gl, gl.FRAGMENT_SHADER, targetFrag); - - // link them together - var shaderProgram = gl.createProgram(); - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - shaderProgram._type = shaderName; - - // check compile status - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { - gl.useProgram(this._activeShader); - throw gl.getProgramInfoLog(shaderProgram); - } - - // 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); - - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - gl.uniform1i(shaderProgram.samplerUniform, 0); - - shaderProgram.uprightUniform = gl.getUniformLocation(shaderProgram, "uUpright"); - gl.uniform1f(shaderProgram.uprightUniform, 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; - } - - shaderProgram.samplerData = samplers; - shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler"); - gl.uniform1iv(shaderProgram.samplerUniform, samplers); - - shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix"); - break; - } - - gl.useProgram(this._activeShader); - return shaderProgram; - }; - - /** - * Creates a shader from the specified string replacing templates. Template items are defined via `{{` `key` `}}``. - * @method _createShader - * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. - * @param {Number} type The type of shader to create. gl.VERTEX_SHADER | gl.FRAGMENT_SHADER - * @param {String} str The definition for the shader. - * @return {WebGLShader} - * @protected - */ - p._createShader = function (gl, type, str) { - // inject the static number - str = str.replace(/{{count}}/g, 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 gl.MAX_TEXTURE_SIZE || image.height > gl.MAX_TEXTURE_SIZE){ - console && console.error("Oversized Texture: "+ image.width+"x"+image.height +" vs "+ gl.MAX_TEXTURE_SIZE +"max"); - } - } - }; - - /** - * Adds the texture to a spot in the current batch, forcing a draw if no spots are free. - * @method _insertTextureInBatch - * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into. - * @param {WebGLTexture} texture The texture to be inserted. - * @protected - */ - p._insertTextureInBatch = function (gl, texture) { - // if it wasn't used last batch - if (this._batchTextures[texture._activeIndex] !== texture) { - // we've got to find it a a spot. - var found = -1; - var start = (this._lastTextureInsert+1) % this._batchTextureCount; - var look = start; - do { - if (this._batchTextures[look]._batchID != this._batchID && !this._slotBlacklist[look]) { - found = look; - break; - } - look = (look+1) % this._batchTextureCount; - } while (look !== start); - - // we couldn't find anywhere for it go, meaning we're maxed out - if (found === -1) { - this.batchReason = "textureOverflow"; - this._drawBuffers(gl); // <------------------------------------------------------------------------ - this.batchCardCount = 0; - found = start; - } - - // lets put it into that spot - this._batchTextures[found] = texture; - texture._activeIndex = found; - var image = texture._imageData; - if (image && image._invalid && texture._drawID !== undefined) { - this._updateTextureImageData(gl, image); - } else { - gl.activeTexture(gl.TEXTURE0 + found); - gl.bindTexture(gl.TEXTURE_2D, texture); - this.setTextureParams(gl); - } - this._lastTextureInsert = found; - } else { - var image = texture._imageData; - if (texture._storeID != undefined && image && image._invalid) { - this._updateTextureImageData(gl, image); - } - } - - texture._drawID = this._drawID; - texture._batchID = this._batchID; - }; - - /** - * Remove and clean the texture, expects a texture and is inflexible. Mostly for internal use, recommended to call - * {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} instead as it will call this with the correct texture object(s). - * Note: Testing shows this may not happen immediately, have to wait frames for WebGL to have actually adjust memory. - * @method _killTextureObject - * @param {Texture} tex The texture to be cleaned out - * @protected - */ - p._killTextureObject = function (tex) { - if (!tex) { return; } - var gl = this._webGLContext; - - // remove linkage - if (tex._storeID !== undefined && tex._storeID >= 0) { - this._textureDictionary[tex._storeID] = undefined; - for (var n in this._textureIDs) { - if (this._textureIDs[n] == tex._storeID) { delete this._textureIDs[n]; } - } - if(tex._imageData) { tex._imageData._storeID = undefined; } - tex._imageData = tex._storeID = undefined; - } - - // make sure to drop it out of an active slot - if (tex._activeIndex !== undefined && this._batchTextures[tex._activeIndex] === tex) { - this._batchTextures[tex._activeIndex] = this._baseTextures[tex._activeIndex]; - } - - // remove buffers if present - try { - if (tex._frameBuffer) { gl.deleteFramebuffer(tex._frameBuffer); } - tex._frameBuffer = undefined; - } catch(e) { - /* suppress delete errors because it's already gone or didn't need deleting probably */ - if (this.vocalDebug) { console.log(e); } - } - - // remove entry - try { - gl.deleteTexture(tex); - } catch(e) { - /* suppress delete errors because it's already gone or didn't need deleting probably */ - if (this.vocalDebug) { console.log(e); } - } - }; - - /** - * 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. - * @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); - } - this._isDrawing++; - this._drawID++; - - this.batchCardCount = 0; - this.depth = 0; - - this._appendToBatchGroup(sceneGraph, gl, new createjs.Matrix2D(), this.alpha, ignoreCache); - - this.batchReason = "drawFinish"; - this._drawBuffers(gl); // <-------------------------------------------------------- - this._isDrawing--; - }; - - /** - * 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 - * @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; - - this._backupBatchTextures(false); - - 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); - } - } - - this._backupBatchTextures(true); - - this.protectTextureSlot(lastTextureSlot, false); - this._activeShader = shaderBackup; - this._slotBlacklist = blackListBackup; - }; - - /** - * 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 - */ - p._drawFilters = function (target, filters, manager) { - 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); - - // 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); - 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; - } - - // 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); - - // draw result to canvas R -> C - this._activeShader = this.getFilterShader(this); - gl.clear(gl.COLOR_BUFFER_BIT); - this._drawCover(gl, flipY); - } 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; - } - }; - - /** - * 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 - * @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 - * @protected - */ - p._appendToBatchGroup = function (container, gl, concatMtx, concatAlpha, ignoreCache) { - // sort out shared properties - if (!container._glMtx) { container._glMtx = new createjs.Matrix2D(); } - var cMtx = container._glMtx; - cMtx.copy(concatMtx); - if (container.transformMatrix) { - cMtx.appendMatrix(container.transformMatrix); - } else { - cMtx.appendTransform( - container.x, container.y, - container.scaleX, container.scaleY, - container.rotation, container.skewX, container.skewY, - container.regX, container.regY - ); - } - - // sub components of figuring out the position an object holds - var subL, subT, subR, subB; - - // actually apply its data to the buffers - var l = container.children.length; - for (var i = 0; i < l; i++) { - var item = container.children[i]; - - if (!(item.visible && concatAlpha)) { continue; } - if (!item.cacheCanvas || ignoreCache) { - if (item._updateState){ - item._updateState(); - } - if (item.children) { - this._appendToBatchGroup(item, gl, cMtx, item.alpha * concatAlpha); - continue; - } - } - - // 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) { - this.batchReason = "vertexOverflow"; - this._drawBuffers(gl); // <------------------------------------------------------------ - this.batchCardCount = 0; - } - - // keep track of concatenated position - if (!item._glMtx) { item._glMtx = new createjs.Matrix2D(); } - var iMtx = item._glMtx; - iMtx.copy(cMtx); - if (item.transformMatrix) { - iMtx.appendMatrix(item.transformMatrix); - } else { - iMtx.appendTransform( - item.x, item.y, - item.scaleX, item.scaleY, - item.rotation, item.skewX, item.skewY, - item.regX, item.regY - ); - } - - var uvRect, texIndex, image, frame, texture, src; - var useCache = item.cacheCanvas && !ignoreCache; - - 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? - if (frame === null) { continue; } - image = frame.image; - } else { // MISC (DOM objects render themselves later) - continue; - } - - var uvs = this._uvs; - var vertices = this._vertices; - var texI = this._indices; - var alphas = this._alphas; - - // calculate texture - if (!image) { continue; } - if (image._storeID === undefined) { - // this texture is new to us so load it and add it to the batch - texture = this._loadTextureImage(gl, image); - this._insertTextureInBatch(gl, texture); - } else { - // fetch the texture (render textures know how to look themselves up to simplify this logic) - texture = this._textureDictionary[image._storeID]; - if (!texture){ - if (this.vocalDebug){ console.log("Texture should not be looked up while not being stored."); } - continue; - } - - // put it in the batch if needed - if (texture._batchID !== this._batchID) { - this._insertTextureInBatch(gl, texture); - } - } - texIndex = texture._activeIndex; - - if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas - if (!useCache && item.sourceRect) { - // calculate uvs - if (!item._uvRect) { item._uvRect = {}; } - src = item.sourceRect; - uvRect = item._uvRect; - uvRect.t = (src.y)/image.height; - uvRect.l = (src.x)/image.width; - uvRect.b = (src.y + src.height)/image.height; - uvRect.r = (src.x + src.width)/image.width; - - // calculate vertices - subL = 0; subT = 0; - subR = src.width+subL; subB = src.height+subT; - } else { - // calculate uvs - uvRect = StageGL.UV_RECT; - // calculate vertices - if (useCache) { - 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 - var rect = frame.rect; - - // calculate uvs - uvRect = frame.uvRect; - if (!uvRect) { - uvRect = StageGL.buildUVRects(item.spriteSheet, item.currentFrame, false); - } - - // calculate vertices - subL = -frame.regX; subT = -frame.regY; - subR = rect.width-frame.regX; subB = rect.height-frame.regY; - } - - // 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 - - //DHG: See Matrix2D.transformPoint for why this math specifically - // apply vertices - vertices[offV2] = subL *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+1] = subL *iMtx.b + subT *iMtx.d +iMtx.ty; - vertices[offV2+2] = subL *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+3] = subL *iMtx.b + subB *iMtx.d +iMtx.ty; - vertices[offV2+4] = subR *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+5] = subR *iMtx.b + subT *iMtx.d +iMtx.ty; - vertices[offV2+6] = vertices[offV2+2]; vertices[offV2+7] = vertices[offV2+3]; - vertices[offV2+8] = vertices[offV2+4]; vertices[offV2+9] = vertices[offV2+5]; - vertices[offV2+10] = subR *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+11] = subR *iMtx.b + subB *iMtx.d +iMtx.ty; - - // apply uvs - uvs[offV2] = uvRect.l; uvs[offV2+1] = uvRect.t; - uvs[offV2+2] = uvRect.l; uvs[offV2+3] = uvRect.b; - uvs[offV2+4] = uvRect.r; uvs[offV2+5] = uvRect.t; - uvs[offV2+6] = uvRect.l; uvs[offV2+7] = uvRect.b; - uvs[offV2+8] = uvRect.r; uvs[offV2+9] = uvRect.t; - uvs[offV2+10] = uvRect.r; uvs[offV2+11] = uvRect.b; - - // apply texture - texI[offV1] = texI[offV1+1] = texI[offV1+2] = texI[offV1+3] = texI[offV1+4] = texI[offV1+5] = texIndex; - - // apply alpha - alphas[offV1] = alphas[offV1+1] = alphas[offV1+2] = alphas[offV1+3] = alphas[offV1+4] = alphas[offV1+5] = item.alpha * concatAlpha; - - this.batchCardCount++; - } - }; - - /** - * 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. - * @protected - */ - p._drawBuffers = function (gl) { - if (this.batchCardCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content. - - if (this.vocalDebug) { - console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason); - } - var shaderProgram = this._activeShader; - var vertexPositionBuffer = this._vertexPositionBuffer; - var textureIndexBuffer = this._textureIndexBuffer; - var uvPositionBuffer = this._uvPositionBuffer; - var alphaBuffer = this._alphaBuffer; - - gl.useProgram(shaderProgram); - - gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); - gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._vertices); - - gl.bindBuffer(gl.ARRAY_BUFFER, textureIndexBuffer); - gl.vertexAttribPointer(shaderProgram.textureIndexAttribute, textureIndexBuffer.itemSize, gl.FLOAT, false, 0, 0); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._indices); - - gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer); - gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._uvs); - - gl.bindBuffer(gl.ARRAY_BUFFER, alphaBuffer); - gl.vertexAttribPointer(shaderProgram.alphaAttribute, alphaBuffer.itemSize, gl.FLOAT, false, 0, 0); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._alphas); - - gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, gl.FALSE, this._projectionMatrix); - - for (var i = 0; i < this._batchTextureCount; i++) { - var texture = this._batchTextures[i]; - gl.activeTexture(gl.TEXTURE0 + i); - gl.bindTexture(gl.TEXTURE_2D, texture); - this.setTextureParams(gl, texture.isPOT); - } - - gl.drawArrays(gl.TRIANGLES, 0, this.batchCardCount*StageGL.INDICIES_PER_CARD); - 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. - * @protected - */ - p._drawCover = function (gl, flipY) { - if (this._isDrawing > 0) { - this._drawBuffers(gl); - } - - if (this.vocalDebug) { - console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ "Cover"); - } - 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); - gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); - 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.uniform1i(shaderProgram.samplerUniform, 0); - gl.uniform1f(shaderProgram.uprightUniform, flipY?0:1); - - gl.drawArrays(gl.TRIANGLES, 0, StageGL.INDICIES_PER_CARD); - }; - - createjs.StageGL = createjs.promote(StageGL, "Stage"); -}()); - -//############################################################################## -// Bitmap.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - - /** - * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing - * HTML element, or a string. - * - *

      Example

      - * - * var bitmap = new createjs.Bitmap("imagePath.jpg"); - * - * Notes: - *
        - *
      1. When using a video source that may loop or seek, use a {{#crossLink "VideoBuffer"}}{{/crossLink}} object to - * prevent blinking / flashing. - *
      2. When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it - * will be displayed.
      3. - *
      4. Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this, - * the Bitmap can be cached.
      5. - *
      6. Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This - * happens in all browsers except recent Firefox builds.
      7. - *
      8. Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using - * methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting - * `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`
      9. - *
      - * - * @class Bitmap - * @extends DisplayObject - * @constructor - * @param {CanvasImageSource | String | Object} imageOrUri The source image to display. This can be a CanvasImageSource - * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image. - * If the latter, a new Image instance with the URL as its src will be used. - **/ - function Bitmap(imageOrUri) { - this.DisplayObject_constructor(); - - - // public properties: - /** - * The source image to display. This can be a CanvasImageSource - * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image. - * If the latter, a new Image instance with the URL as its src will be used. - * @property image - * @type CanvasImageSource | Object - **/ - if (typeof imageOrUri == "string") { - this.image = document.createElement("img"); - this.image.src = imageOrUri; - } else { - this.image = imageOrUri; - } - - /** - * Specifies an area of the source image to draw. If omitted, the whole image will be drawn. - * Notes: - *
        - *
      • that video sources must have a width / height set to work correctly with `sourceRect`
      • - *
      • Cached objects will ignore the `sourceRect` property
      • - *
      - * @property sourceRect - * @type Rectangle - * @default null - */ - this.sourceRect = null; - - // private properties: - /** - * Docced in superclass. - */ - this._webGLRenderStyle = createjs.DisplayObject._StageGL_BITMAP; - } - var p = createjs.extend(Bitmap, createjs.DisplayObject); - - -// public methods: - /** - * Constructor alias for backwards compatibility. This method will be removed in future versions. - * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. - * @method initialize - * @deprecated in favour of `createjs.promote()` - **/ - p.initialize = Bitmap; // TODO: deprecated. - - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var image = this.image; - var hasContent = this.cacheCanvas || (image && (image.naturalWidth || image.getContext || image.readyState >= 2)); - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); - }; - - /** - * Draws the display object 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). - * @return {Boolean} - **/ - p.draw = function(ctx, ignoreCache) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - var img = this.image, rect = this.sourceRect; - if (img.getImage) { img = img.getImage(); } - if (!img) { return true; } - if (rect) { - // some browsers choke on out of bound values, so we'll fix them: - var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height; - if (x1 < 0) { x -= x1; x1 = 0; } - if (x2 > w) { x2 = w; } - if (y1 < 0) { y -= y1; y1 = 0; } - if (y2 > h) { y2 = h; } - ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1); - } else { - ctx.drawImage(img, 0, 0); - } - return true; - }; - - //Note, the doc sections below document using the specified APIs (from DisplayObject) from - //Bitmap. This is why they have no method implementations. - - /** - * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. - * You should not cache Bitmap instances as it can degrade performance. - * - * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. - * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} - * method. - * @method cache - **/ - - /** - * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. - * You should not cache Bitmap instances as it can degrade performance. - * - * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. - * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} - * method. - * @method updateCache - **/ - - /** - * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances. - * You should not cache Bitmap instances as it can degrade performance. - * - * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work. - * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}} - * method. - * @method uncache - **/ - - /** - * Docced in superclass. - */ - p.getBounds = function() { - var rect = this.DisplayObject_getBounds(); - if (rect) { return rect; } - var image = this.image, o = this.sourceRect || image; - var hasContent = (image && (image.naturalWidth || image.getContext || image.readyState >= 2)); - return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null; - }; - - /** - * Returns a clone of the Bitmap instance. - * @method clone - * @param {Boolean} node Whether the underlying dom element should be cloned as well. - * @return {Bitmap} a clone of the Bitmap instance. - **/ - p.clone = function(node) { - var image = this.image; - if(image && node){ image = image.cloneNode(); } - var o = new Bitmap(image); - if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); } - this._cloneProps(o); - return o; - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Bitmap (name="+ this.name +")]"; - }; - - - createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject"); -}()); - -//############################################################################## -// Sprite.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of - * images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100 - * images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames, - * play frames as an animation, and even sequence animations together. - * - * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations. - * - *

      Example

      - * - * var instance = new createjs.Sprite(spriteSheet); - * instance.gotoAndStop("frameName"); - * - * Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called, - * only the first defined frame defined in the sprite sheet will be displayed. - * - * @class Sprite - * @extends DisplayObject - * @constructor - * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame - * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information. - * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially. - **/ - function Sprite(spriteSheet, frameOrAnimation) { - this.DisplayObject_constructor(); - - - // public properties: - /** - * The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}} - * definitions, this will advance non-sequentially. This will always be an integer value. - * @property currentFrame - * @type {Number} - * @default 0 - * @readonly - **/ - this.currentFrame = 0; - - /** - * Returns the name of the currently playing animation. - * @property currentAnimation - * @type {String} - * @final - * @readonly - **/ - this.currentAnimation = null; - - /** - * Prevents the animation from advancing each tick automatically. For example, you could create a sprite - * sheet of icons, set paused to true, and display the appropriate icon by setting currentFrame. - * @property paused - * @type {Boolean} - * @default false - **/ - this.paused = true; - - /** - * The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame - * data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information. - * @property spriteSheet - * @type {SpriteSheet} - * @readonly - **/ - this.spriteSheet = spriteSheet; - - /** - * Specifies the current frame index within the currently playing animation. When playing normally, this will increase - * from 0 to n-1, where n is the number of frames in the current animation. - * - * This could be a non-integer value if - * using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is - * not an integer. - * @property currentAnimationFrame - * @type {Number} - * @default 0 - **/ - this.currentAnimationFrame = 0; - - /** - * By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related - * SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target - * framerate. - * - * For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will - * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will - * vary slightly between frames. - * - * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being - * passed into {{#crossLink "Stage/update"}}{{/crossLink}}. - * @property framerate - * @type {Number} - * @default 0 - **/ - this.framerate = 0; - - - // private properties: - /** - * Current animation object. - * @property _animation - * @protected - * @type {Object} - * @default null - **/ - this._animation = null; - - /** - * Current frame index. - * @property _currentFrame - * @protected - * @type {Number} - * @default null - **/ - this._currentFrame = null; - - /** - * Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame - * @property _skipAdvance - * @protected - * @type {Boolean} - * @default false - **/ - this._skipAdvance = false; - - /** - * Docced in superclass. - */ - this._webGLRenderStyle = createjs.DisplayObject._StageGL_SPRITE; - - if (frameOrAnimation != null) { this.gotoAndPlay(frameOrAnimation); } - } - var p = createjs.extend(Sprite, createjs.DisplayObject); - - /** - * Constructor alias for backwards compatibility. This method will be removed in future versions. - * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. - * @method initialize - * @deprecated in favour of `createjs.promote()` - **/ - p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of Flash/Animate spritesheet export. - - -// events: - /** - * Dispatched when an animation reaches its ends. - * @event animationend - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @param {String} name The name of the animation that just ended. - * @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping. - * @since 0.6.0 - */ - - /** - * Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick, - * or calling gotoAndPlay() or gotoAndStop(). - * @event change - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - */ - - -// public methods: - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var hasContent = this.cacheCanvas || this.spriteSheet.complete; - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); - }; - - /** - * Draws the display object 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 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) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - this._normalizeFrame(); - var o = this.spriteSheet.getFrame(this._currentFrame|0); - if (!o) { return false; } - var rect = o.rect; - if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); } - return true; - }; - - //Note, the doc sections below document using the specified APIs (from DisplayObject) from - //Bitmap. This is why they have no method implementations. - - /** - * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. - * You should not cache Sprite instances as it can degrade performance. - * @method cache - **/ - - /** - * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. - * You should not cache Sprite instances as it can degrade performance. - * @method updateCache - **/ - - /** - * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances. - * You should not cache Sprite instances as it can degrade performance. - * @method uncache - **/ - - /** - * Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}} - * or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain - * unchanged. - * @method play - **/ - p.play = function() { - this.paused = false; - }; - - /** - * Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} - * is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}} - * will resume playback. - * @method stop - **/ - p.stop = function() { - this.paused = true; - }; - - /** - * Sets paused to false and plays the specified animation name, named frame, or frame number. - * @method gotoAndPlay - * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to - * and begin playing. - **/ - p.gotoAndPlay = function(frameOrAnimation) { - this.paused = false; - this._skipAdvance = true; - this._goto(frameOrAnimation); - }; - - /** - * Sets paused to true and seeks to the specified animation name, named frame, or frame number. - * @method gotoAndStop - * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to - * and stop. - **/ - p.gotoAndStop = function(frameOrAnimation) { - this.paused = true; - this._goto(frameOrAnimation); - }; - - /** - * Advances the playhead. This occurs automatically each tick by default. - * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite - * or its SpriteSheet. - * @method advance - */ - p.advance = function(time) { - var fps = this.framerate || this.spriteSheet.framerate; - var t = (fps && time != null) ? time/(1000/fps) : 1; - this._normalizeFrame(t); - }; - - /** - * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to - * the origin. For example, a 90 x 70 frame with regX=50 and regY=40 would return a - * rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object. - * - * Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method. - * @method getBounds - * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully - * loaded. - **/ - p.getBounds = function() { - // TODO: should this normalizeFrame? - return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle); - }; - - /** - * Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned - * instances. - * @method clone - * @return {Sprite} a clone of the Sprite instance. - **/ - p.clone = function() { - return this._cloneProps(new Sprite(this.spriteSheet)); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Sprite (name="+ this.name +")]"; - }; - -// private methods: - /** - * @method _cloneProps - * @param {Sprite} o - * @return {Sprite} o - * @protected - **/ - p._cloneProps = function(o) { - this.DisplayObject__cloneProps(o); - o.currentFrame = this.currentFrame; - o.currentAnimation = this.currentAnimation; - o.paused = this.paused; - o.currentAnimationFrame = this.currentAnimationFrame; - o.framerate = this.framerate; - - o._animation = this._animation; - o._currentFrame = this._currentFrame; - o._skipAdvance = this._skipAdvance; - return o; - }; - - /** - * Advances the currentFrame if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}} - * ticks. - * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. - * @protected - * @method _tick - **/ - p._tick = function(evtObj) { - if (!this.paused) { - if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); } - this._skipAdvance = false; - } - this.DisplayObject__tick(evtObj); - }; - - - /** - * Normalizes the current frame, advancing animations and dispatching callbacks as appropriate. - * @protected - * @method _normalizeFrame - **/ - p._normalizeFrame = function(frameDelta) { - frameDelta = frameDelta || 0; - var animation = this._animation; - var paused = this.paused; - var frame = this._currentFrame; - var l; - - if (animation) { - var speed = animation.speed || 1; - var animFrame = this.currentAnimationFrame; - l = animation.frames.length; - if (animFrame + frameDelta * speed >= l) { - var next = animation.next; - if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) { - // something changed in the event stack, so we shouldn't make any more changes here. - return; - } else if (next) { - // sequence. Automatically calls _normalizeFrame again with the remaining frames. - return this._goto(next, frameDelta - (l - animFrame) / speed); - } else { - // end. - this.paused = true; - animFrame = animation.frames.length - 1; - } - } else { - animFrame += frameDelta * speed; - } - this.currentAnimationFrame = animFrame; - this._currentFrame = animation.frames[animFrame | 0] - } else { - frame = (this._currentFrame += frameDelta); - l = this.spriteSheet.getNumFrames(); - if (frame >= l && l > 0) { - if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) { - // looped. - if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); } - } - } - } - frame = this._currentFrame | 0; - if (this.currentFrame != frame) { - this.currentFrame = frame; - this.dispatchEvent("change"); - } - }; - - /** - * Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}}, - * {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.) - * @property _dispatchAnimationEnd - * @private - * @type {Function} - **/ - p._dispatchAnimationEnd = function(animation, frame, paused, next, end) { - var name = animation ? animation.name : null; - if (this.hasEventListener("animationend")) { - var evt = new createjs.Event("animationend"); - evt.name = name; - evt.next = next; - this.dispatchEvent(evt); - } - // did the animation get changed in the event stack?: - var changed = (this._animation != animation || this._currentFrame != frame); - // if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame: - if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; } - return changed; - }; - - /** - * Moves the playhead to the specified frame number or animation. - * @method _goto - * @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to. - * @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0. - * @protected - **/ - p._goto = function(frameOrAnimation, frame) { - this.currentAnimationFrame = 0; - if (isNaN(frameOrAnimation)) { - var data = this.spriteSheet.getAnimation(frameOrAnimation); - if (data) { - this._animation = data; - this.currentAnimation = frameOrAnimation; - this._normalizeFrame(frame); - } - } else { - this.currentAnimation = this._animation = null; - this._currentFrame = frameOrAnimation; - this._normalizeFrame(); - } - }; - - - createjs.Sprite = createjs.promote(Sprite, "DisplayObject"); -}()); - -//############################################################################## -// Shape.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}} - * instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape - * instances to display the same vector graphics with different positions or transforms. - * - * If the vector art will not - * change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the - * rendering cost. - * - *

      Example

      - * - * var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100); - * var shape = new createjs.Shape(graphics); - * - * //Alternatively use can also use the graphics property of the Shape class to renderer the same as above. - * var shape = new createjs.Shape(); - * shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100); - * - * @class Shape - * @extends DisplayObject - * @constructor - * @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created. - **/ - function Shape(graphics) { - this.DisplayObject_constructor(); - - - // public properties: - /** - * The graphics instance to display. - * @property graphics - * @type Graphics - **/ - this.graphics = graphics ? graphics : new createjs.Graphics(); - } - var p = createjs.extend(Shape, createjs.DisplayObject); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// public methods: - /** - * Returns true or false indicating whether the Shape would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty()); - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); - }; - - /** - * Draws the Shape 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). - * @return {Boolean} - **/ - p.draw = function(ctx, ignoreCache) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - this.graphics.draw(ctx, this); - return true; - }; - - /** - * Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to - * their defaults (for example .parent). - * @method clone - * @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be - * cloned. If false, the Graphics instance will be shared with the new Shape. - **/ - p.clone = function(recursive) { - var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics; - return this._cloneProps(new Shape(g)); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Shape (name="+ this.name +")]"; - }; - - - createjs.Shape = createjs.promote(Shape, "DisplayObject"); -}()); - -//############################################################################## -// Text.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the - * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML - * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} - * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}. - * - * Please note that Text does not support HTML text, and can only display one font style at a time. To use - * multiple font styles, you will need to create multiple text instances, and position them manually. - * - *

      Example

      - * - * var text = new createjs.Text("Hello World", "20px Arial", "#ff7700"); - * text.x = 100; - * text.textBaseline = "alphabetic"; - * - * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser - * before it can be displayed. - * - * Note: Text can be expensive to generate, so cache instances where possible. Be aware that not all - * browsers will render Text exactly the same. - * @class Text - * @extends DisplayObject - * @constructor - * @param {String} [text] The text to display. - * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold - * 36px Arial"). - * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. - * "#F00", "red", or "#FF0000"). - **/ - function Text(text, font, color) { - this.DisplayObject_constructor(); - - - // public properties: - /** - * The text to display. - * @property text - * @type String - **/ - this.text = text; - - /** - * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial"). - * @property font - * @type String - **/ - this.font = font; - - /** - * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000". - * It will also accept valid canvas fillStyle values. - * @property color - * @type String - **/ - this.color = color; - - /** - * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed - * information view the - * - * whatwg spec. Default is "left". - * @property textAlign - * @type String - **/ - this.textAlign = "left"; - - /** - * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or - * "bottom". For detailed information view the - * whatwg spec. Default is "top". - * @property textBaseline - * @type String - */ - this.textBaseline = "top"; - - /** - * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or - * shrunk to make it fit in this width. For detailed information view the - * - * whatwg spec. - * @property maxWidth - * @type Number - */ - this.maxWidth = null; - - /** - * If greater than 0, the text will be drawn as a stroke (outline) of the specified width. - * @property outline - * @type Number - **/ - this.outline = 0; - - /** - * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0, - * the value of getMeasuredLineHeight is used. - * @property lineHeight - * @type Number - **/ - this.lineHeight = 0; - - /** - * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null, - * the text will not be wrapped. - * @property lineWidth - * @type Number - **/ - this.lineWidth = null; - } - var p = createjs.extend(Text, createjs.DisplayObject); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// static properties: - /** - * @property _workingContext - * @type CanvasRenderingContext2D - * @private - **/ - var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); - if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; } - - -// constants: - /** - * Lookup table for the ratio to offset bounds x calculations based on the textAlign property. - * @property H_OFFSETS - * @type Object - * @protected - * @static - **/ - Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1}; - - /** - * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property. - * @property H_OFFSETS - * @type Object - * @protected - * @static - **/ - Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1}; - - -// public methods: - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var hasContent = this.cacheCanvas || (this.text != null && this.text !== ""); - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); - }; - - /** - * Draws the Text 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 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) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - - var col = this.color || "#000"; - if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; } - else { ctx.fillStyle = col; } - - this._drawText(this._prepContext(ctx)); - return true; - }; - - /** - * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value. - * @method getMeasuredWidth - * @return {Number} The measured, untransformed width of the text. - **/ - p.getMeasuredWidth = function() { - return this._getMeasuredWidth(this.text); - }; - - /** - * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured - * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts. - * @method getMeasuredLineHeight - * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is - * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts. - **/ - p.getMeasuredLineHeight = function() { - return this._getMeasuredWidth("M")*1.2; - }; - - /** - * Returns the approximate height of multi-line text by multiplying the number of lines against either the - * lineHeight (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that - * this operation requires the text flowing logic to run, which has an associated CPU cost. - * @method getMeasuredHeight - * @return {Number} The approximate height of the untransformed multi-line text. - **/ - p.getMeasuredHeight = function() { - return this._drawText(null,{}).height; - }; - - /** - * Docced in superclass. - */ - p.getBounds = function() { - var rect = this.DisplayObject_getBounds(); - if (rect) { return rect; } - if (this.text == null || this.text === "") { return null; } - var o = this._drawText(null, {}); - var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width; - var x = w * Text.H_OFFSETS[this.textAlign||"left"]; - var lineHeight = this.lineHeight||this.getMeasuredLineHeight(); - var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"]; - return this._rectangle.setValues(x, y, w, o.height); - }; - - /** - * Returns an object with width, height, and lines properties. The width and height are the visual width and height - * of the drawn text. The lines property contains an array of strings, one for - * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing - * whitespace removed. - * @method getMetrics - * @return {Object} An object with width, height, and lines properties. - **/ - p.getMetrics = function() { - var o = {lines:[]}; - o.lineHeight = this.lineHeight || this.getMeasuredLineHeight(); - o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"]; - return this._drawText(null, o, o.lines); - }; - - /** - * Returns a clone of the Text instance. - * @method clone - * @return {Text} a clone of the Text instance. - **/ - p.clone = function() { - return this._cloneProps(new Text(this.text, this.font, this.color)); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Text (text="+ (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]"; - }; - - -// private methods: - /** - * @method _cloneProps - * @param {Text} o - * @protected - * @return {Text} o - **/ - p._cloneProps = function(o) { - this.DisplayObject__cloneProps(o); - o.textAlign = this.textAlign; - o.textBaseline = this.textBaseline; - o.maxWidth = this.maxWidth; - o.outline = this.outline; - o.lineHeight = this.lineHeight; - o.lineWidth = this.lineWidth; - return o; - }; - - /** - * @method _getWorkingContext - * @param {CanvasRenderingContext2D} ctx - * @return {CanvasRenderingContext2D} - * @protected - **/ - p._prepContext = function(ctx) { - ctx.font = this.font||"10px sans-serif"; - ctx.textAlign = this.textAlign||"left"; - ctx.textBaseline = this.textBaseline||"top"; - ctx.lineJoin = "miter"; - ctx.miterLimit = 2.5; - return ctx; - }; - - /** - * Draws multiline text. - * @method _drawText - * @param {CanvasRenderingContext2D} ctx - * @param {Object} o - * @param {Array} lines - * @return {Object} - * @protected - **/ - p._drawText = function(ctx, o, lines) { - var paint = !!ctx; - if (!paint) { - ctx = Text._workingContext; - ctx.save(); - this._prepContext(ctx); - } - var lineHeight = this.lineHeight||this.getMeasuredLineHeight(); - - var maxW = 0, count = 0; - var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/); - for (var i=0, l=hardLines.length; i this.lineWidth) { - // text wrapping: - var words = str.split(/(\s)/); - str = words[0]; - w = ctx.measureText(str).width; - - for (var j=1, jl=words.length; j this.lineWidth) { - if (paint) { this._drawTextLine(ctx, str, count*lineHeight); } - if (lines) { lines.push(str); } - if (w > maxW) { maxW = w; } - str = words[j+1]; - w = ctx.measureText(str).width; - count++; - } else { - str += words[j] + words[j+1]; - w += wordW; - } - } - } - - if (paint) { this._drawTextLine(ctx, str, count*lineHeight); } - if (lines) { lines.push(str); } - if (o && w == null) { w = ctx.measureText(str).width; } - if (w > maxW) { maxW = w; } - count++; - } - - if (o) { - o.width = maxW; - o.height = count*lineHeight; - } - if (!paint) { ctx.restore(); } - return o; - }; - - /** - * @method _drawTextLine - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} y - * @protected - **/ - p._drawTextLine = function(ctx, text, y) { - // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead: - if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); } - else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); } - }; - - - /** - * @method _getMeasuredWidth - * @param {String} text - * @protected - **/ - p._getMeasuredWidth = function(text) { - var ctx = Text._workingContext; - ctx.save(); - var w = this._prepContext(ctx).measureText(text).width; - ctx.restore(); - return w; - }; - - - createjs.Text = createjs.promote(Text, "DisplayObject"); -}()); - -//############################################################################## -// BitmapText.js -//############################################################################## - -this.createjs = this.createjs || {}; - -(function () { - "use strict"; - - -// constructor: - /** - * Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported using new line characters, - * but automatic wrapping is not supported. See the {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}} - * property for more information on defining glyphs. - * - * Important: While BitmapText extends Container, it is not designed to be used as one. - * As such, methods like addChild and removeChild are disabled. - * - * - * @class BitmapText - * @extends DisplayObject - * @param {String} [text=""] The text to display. - * @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs. - * @constructor - **/ - function BitmapText(text, spriteSheet) { - this.Container_constructor(); - - - // public properties: - /** - * The text to display. - * @property text - * @type String - * @default "" - **/ - this.text = text||""; - - /** - * A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character - * should have a single frame animation defined in the sprite sheet named the same as - * corresponding character. For example, the following animation definition: - * - * "A": {frames: [0]} - * - * would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form - * is also acceptable: - * - * "A": 0 - * - * Note that if a character in the text is not found in the sprite sheet, it will also - * try to use the alternate case (upper or lower). - * - * See SpriteSheet for more information on defining sprite sheet data. - * @property spriteSheet - * @type SpriteSheet - * @default null - **/ - this.spriteSheet = spriteSheet; - - /** - * The height of each line of text. If 0, then it will use a line height calculated - * by checking for the height of the "1", "T", or "L" character (in that order). If - * those characters are not defined, it will use the height of the first frame of the - * sprite sheet. - * @property lineHeight - * @type Number - * @default 0 - **/ - this.lineHeight = 0; - - /** - * This spacing (in pixels) will be added after each character in the output. - * @property letterSpacing - * @type Number - * @default 0 - **/ - this.letterSpacing = 0; - - /** - * If a space character is not defined in the sprite sheet, then empty pixels equal to - * spaceWidth will be inserted instead. If 0, then it will use a value calculated - * by checking for the width of the "1", "l", "E", or "A" character (in that order). If - * those characters are not defined, it will use the width of the first frame of the - * sprite sheet. - * @property spaceWidth - * @type Number - * @default 0 - **/ - this.spaceWidth = 0; - - - // private properties: - /** - * @property _oldProps - * @type Object - * @protected - **/ - this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0}; - - /** - * Used to track the object which this class attached listeners to, helps optimize listener attachment. - * @property _oldStage - * @type Stage - * @protected - */ - this._oldStage = null; - /** - * The event listener proxy triggered drawing draw for special circumstances. - * @property _drawAction - * @type function - * @protected - */ - this._drawAction = null; - } - var p = createjs.extend(BitmapText, createjs.Container); - -// static properties: - /** - * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains - * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be - * retained, slightly increasing memory use, but reducing instantiation. - * @property maxPoolSize - * @type Number - * @static - * @default 100 - **/ - BitmapText.maxPoolSize = 100; - - /** - * Sprite object pool. - * @type {Array} - * @static - * @private - */ - BitmapText._spritePool = []; - - -// public methods: - /** - * Docced in superclass. - **/ - p.draw = function(ctx, ignoreCache) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return; } - this._updateState(); - this.Container_draw(ctx, ignoreCache); - }; - - /** - * Docced in superclass. - **/ - p.getBounds = function() { - this._updateText(); - return this.Container_getBounds(); - }; - - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text); - return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent); - }; - - p.clone = function() { - return this._cloneProps(new BitmapText(this.text, this.spriteSheet)); - }; - - /** - * Disabled in BitmapText. - * @method addChild - **/ - /** - * Disabled in BitmapText. - * @method addChildAt - **/ - /** - * Disabled in BitmapText. - * @method removeChild - **/ - /** - * Disabled in BitmapText. - * @method removeChildAt - **/ - /** - * Disabled in BitmapText. - * @method removeAllChildren - **/ - p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {}; - - -// private methods: - /** - * Docced in superclass. - **/ - p._updateState = function() { - this._updateText(); - }; - - /** - * @method _cloneProps - * @param {BitmapText} o - * @return {BitmapText} o - * @protected - **/ - p._cloneProps = function(o) { - this.Container__cloneProps(o); - o.lineHeight = this.lineHeight; - o.letterSpacing = this.letterSpacing; - o.spaceWidth = this.spaceWidth; - return o; - }; - - /** - * @method _getFrameIndex - * @param {String} character - * @param {SpriteSheet} spriteSheet - * @return {Number} - * @protected - **/ - p._getFrameIndex = function(character, spriteSheet) { - var c, o = spriteSheet.getAnimation(character); - if (!o) { - (character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null); - if (c) { o = spriteSheet.getAnimation(c); } - } - return o && o.frames[0]; - }; - - /** - * @method _getFrame - * @param {String} character - * @param {SpriteSheet} spriteSheet - * @return {Object} - * @protected - **/ - p._getFrame = function(character, spriteSheet) { - var index = this._getFrameIndex(character, spriteSheet); - return index == null ? index : spriteSheet.getFrame(index); - }; - - /** - * @method _getLineHeight - * @param {SpriteSheet} ss - * @return {Number} - * @protected - **/ - p._getLineHeight = function(ss) { - var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0); - return frame ? frame.rect.height : 1; - }; - - /** - * @method _getSpaceWidth - * @param {SpriteSheet} ss - * @return {Number} - * @protected - **/ - p._getSpaceWidth = function(ss) { - var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0); - return frame ? frame.rect.width : 1; - }; - - /** - * @method _updateText - * @protected - **/ - p._updateText = function() { - var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet; - var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite; - - for (var n in o) { - if (o[n] != this[n]) { - o[n] = this[n]; - change = true; - } - } - if (!change) { return; } - - var hasSpace = !!this._getFrame(" ", ss); - if (!hasSpace && !spaceW) { spaceW = this._getSpaceWidth(ss); } - if (!lineH) { lineH = this._getLineHeight(ss); } - - for(var i=0, l=this.text.length; i childIndex) { - // faster than removeChild. - pool.push(sprite = kids.pop()); - sprite.parent = null; - numKids--; - } - if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; } - }; - - - createjs.BitmapText = createjs.promote(BitmapText, "Container"); -}()); - -//############################################################################## -// MovieClip.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows - * you to create objects which encapsulate timeline animations, state changes, and synched actions. The MovieClip - * class has been included in the EaselJS minified file since 0.7.0. - * - * Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have - * been made to support time-based timelines in the future. - * - *

      Example

      - * This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in - * the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}. - * - * var stage = new createjs.Stage("canvas"); - * createjs.Ticker.addEventListener("tick", stage); - * - * var mc = new createjs.MovieClip({loop:-1, labels:{myLabel:20}}); - * stage.addChild(mc); - * - * var child1 = new createjs.Shape( - * new createjs.Graphics().beginFill("#999999") - * .drawCircle(30,30,30)); - * var child2 = new createjs.Shape( - * new createjs.Graphics().beginFill("#5a9cfb") - * .drawCircle(30,30,30)); - * - * mc.timeline.addTween( - * createjs.Tween.get(child1) - * .to({x:0}).to({x:60}, 50).to({x:0}, 50)); - * mc.timeline.addTween( - * createjs.Tween.get(child2) - * .to({x:60}).to({x:0}, 50).to({x:60}, 50)); - * - * mc.gotoAndPlay("start"); - * - * It is recommended to use tween.to() to animate and set properties (use no duration to have it set - * immediately), and the tween.wait() method to create delays between animations. Note that using the - * tween.set() method to affect properties will likely not provide the desired result. - * - * @class MovieClip - * @main MovieClip - * @param {Object} [props] The configuration properties to apply to this instance (ex. `{mode:MovieClip.SYNCHED}`). - * Supported props for the MovieClip are listed below. These props are set on the corresponding instance properties except where - * specified.
        - *
      • `mode`
      • - *
      • `startPosition`
      • - *
      • `frameBounds`
      • - *
      - * - * This object will also be passed into the Timeline instance associated with this MovieClip. See the documentation - * for Timeline for a list of supported props (ex. `paused`, `labels`, `loop`, `reversed`, etc.) - * @extends Container - * @constructor - **/ - function MovieClip(props) { - this.Container_constructor(); - !MovieClip.inited&&MovieClip.init(); // static init - - var mode, startPosition, loop, labels; - - // handle old params (mode, startPosition, loop, labels): - // TODO: deprecated param handling: - if (props instanceof String || arguments.length > 1) { - mode = props; - startPosition = arguments[1]; - loop = arguments[2]; - labels = arguments[3]; - if (loop == null) { loop = -1; } - props = null; - } else if (props) { - mode = props.mode; - startPosition = props.startPosition; - loop = props.loop; - labels = props.labels; - } - if (!props) { props = {labels:labels}; } - - - // public properties: - /** - * Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED). - * See each constant for a description of the behaviour. - * @property mode - * @type String - * @default null - **/ - this.mode = mode||MovieClip.INDEPENDENT; - - /** - * Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME. - * @property startPosition - * @type Number - * @default 0 - */ - this.startPosition = startPosition||0; - - /** - * Specifies how many times this MovieClip should loop. A value of -1 indicates it should loop indefinitely. A value of - * 1 would cause it to loop once (ie. play a total of twice). - * @property loop - * @type Number - * @default -1 - */ - this.loop = loop === true ? -1 : (loop || 0); - - /** - * The current frame of the movieclip. - * @property currentFrame - * @type Number - * @default 0 - * @readonly - */ - this.currentFrame = 0; - - /** - * If true, the MovieClip's position will not advance when ticked. - * @property paused - * @type Boolean - * @default false - */ - this.paused = props.paused||false; - - /** - * If true, actions in this MovieClip's tweens will be run when the playhead advances. - * @property actionsEnabled - * @type Boolean - * @default true - */ - this.actionsEnabled = true; - - /** - * If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds - * it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT. - *

      - * For example, if you had a character animation with a "body" child MovieClip instance - * with different costumes on each frame, you could set body.autoReset = false, so that - * you can manually change the frame it is on, without worrying that it will be reset - * automatically. - * @property autoReset - * @type Boolean - * @default true - */ - this.autoReset = true; - - /** - * An array of bounds for each frame in the MovieClip. This is mainly intended for tool output. - * @property frameBounds - * @type Array - * @default null - */ - this.frameBounds = this.frameBounds||props.frameBounds; // frameBounds are set on the prototype in Animate. - - /** - * By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip - * will cause it to advance based on elapsed time between ticks as appropriate to maintain the target - * framerate. - * - * For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will - * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will - * vary slightly between frames. - * - * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being - * passed into {{#crossLink "Stage/update"}}{{/crossLink}}. - * @property framerate - * @type {Number} - * @default null - **/ - this.framerate = null; - - // set up the needed props for Timeline: - props.useTicks = props.paused = true; - - /** - * The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip - * instance is initialized. Animations are created by adding TweenJS Tween - * instances to the timeline. - * - *

      Example

      - * - * var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30); - * var mc = new createjs.MovieClip(); - * mc.timeline.addTween(tween); - * - * Elements can be added and removed from the timeline by toggling an "_off" property - * using the tweenInstance.to() method. Note that using Tween.set is not recommended to - * create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for - * frame 1. You can use the "visible" property to achieve the same effect. - * - * var tween = createjs.Tween.get(target).to({_off:false}) - * .wait(1).to({_off:true}) - * .wait(1).to({_off:false}); - * - * @property timeline - * @type Timeline - * @default null - */ - this.timeline = new createjs.Timeline(props); - - - // private properties: - /** - * @property _synchOffset - * @type Number - * @default 0 - * @private - */ - this._synchOffset = 0; - - /** - * @property _rawPosition - * @type Number - * @default -1 - * @private - */ - this._rawPosition = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1. - - /** - * @property _bound_resolveState - * @type Function - * @private - */ - this._bound_resolveState = this._resolveState.bind(this); - - - /** - * The time remaining from the previous tick, only applicable when .framerate is set. - * @property _t - * @type Number - * @private - */ - this._t = 0; - - /** - * List of display objects that are actively being managed by the MovieClip. - * @property _managed - * @type Object - * @private - */ - this._managed = {}; - } - var p = createjs.extend(MovieClip, createjs.Container); - - -// constants: - /** - * The MovieClip will advance independently of its parent, even if its parent is paused. - * This is the default mode. - * @property INDEPENDENT - * @static - * @type String - * @default "independent" - * @readonly - **/ - MovieClip.INDEPENDENT = "independent"; - - /** - * The MovieClip will only display a single frame (as determined by the startPosition property). - * @property SINGLE_FRAME - * @static - * @type String - * @default "single" - * @readonly - **/ - MovieClip.SINGLE_FRAME = "single"; - - /** - * The MovieClip will be advanced only when its parent advances and will be synched to the position of - * the parent MovieClip. - * @property SYNCHED - * @static - * @type String - * @default "synched" - * @readonly - **/ - MovieClip.SYNCHED = "synched"; - - -// static properties: - MovieClip.inited = false; - - -// static methods: - MovieClip.init = function() { - if (MovieClip.inited) { return; } - // plugins introduce some overhead to Tween, so we only install this if an MC is instantiated. - MovieClipPlugin.install(); - MovieClip.inited = true; - }; - - -// getter / setters: - /** - * Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead. - * @method _getLabels - * @protected - * @return {Array} - **/ - p._getLabels = function() { - return this.timeline.getLabels(); - }; - // MovieClip.getLabels is @deprecated. Remove for 1.1+ - p.getLabels = createjs.deprecate(p._getLabels, "MovieClip.getLabels"); - - /** - * Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead. - * @method _getCurrentLabel - * @protected - * @return {String} - **/ - p._getCurrentLabel = function() { - return this.timeline.currentLabel; - }; - // MovieClip.getCurrentLabel is @deprecated. Remove for 1.1+ - p.getCurrentLabel = createjs.deprecate(p._getCurrentLabel, "MovieClip.getCurrentLabel"); - - /** - * Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead. - * @method _getDuration - * @protected - * @return {Number} - **/ - p._getDuration = function() { - return this.timeline.duration; - }; - // MovieClip.getDuration is @deprecated. Remove for 1.1+ - p.getDuration = createjs.deprecate(p._getDuration, "MovieClip.getDuration"); - - /** - * Returns an array of objects with label and position (aka frame) properties, sorted by position. - * @property labels - * @type {Array} - * @readonly - **/ - - /** - * Returns the name of the label on or immediately before the current frame. - * @property currentLabel - * @type {String} - * @readonly - **/ - - /** - * Returns the duration of this MovieClip in seconds or ticks. - * @property totalFrames - * @type {Number} - * @readonly - **/ - - /** - * Returns the duration of this MovieClip in seconds or ticks. - * @property duration - * @type {Number} - * @readonly - **/ - try { - Object.defineProperties(p, { - labels: { get: p._getLabels }, - currentLabel: { get: p._getCurrentLabel }, - totalFrames: { get: p._getDuration }, - duration: { get: p._getDuration } - // TODO: can we just proxy .currentFrame to tl.position as well? Ditto for .loop (or just remove entirely). - }); - } catch (e) {} - - -// public methods: - /** - * Constructor alias for backwards compatibility. This method will be removed in future versions. - * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}. - * @method initialize - * @deprecated in favour of `createjs.promote()` - **/ - p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of Adobe Flash/Animate - - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - **/ - p.isVisible = function() { - // children are placed in draw, so we can't determine if we have content. - return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0); - }; - - /** - * Draws the display object 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 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) { - // draw to cache first: - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - this._updateState(); - this.Container_draw(ctx, ignoreCache); - return true; - }; - - /** - * Sets paused to false. - * @method play - **/ - p.play = function() { - this.paused = false; - }; - - /** - * Sets paused to true. - * @method stop - **/ - p.stop = function() { - this.paused = true; - }; - - /** - * Advances this movie clip to the specified position or label and sets paused to false. - * @method gotoAndPlay - * @param {String|Number} positionOrLabel The animation name or frame number to go to. - **/ - p.gotoAndPlay = function(positionOrLabel) { - this.paused = false; - this._goto(positionOrLabel); - }; - - /** - * Advances this movie clip to the specified position or label and sets paused to true. - * @method gotoAndStop - * @param {String|Number} positionOrLabel The animation or frame name to go to. - **/ - p.gotoAndStop = function(positionOrLabel) { - this.paused = true; - this._goto(positionOrLabel); - }; - - /** - * Advances the playhead. This occurs automatically each tick by default. - * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set. - * @method advance - */ - p.advance = function(time) { - var independent = MovieClip.INDEPENDENT; - if (this.mode !== independent) { return; } // update happens in draw for synched clips - - // if this MC doesn't have a framerate, hunt ancestors for one: - var o=this, fps = o.framerate; - while ((o = o.parent) && fps === null) { if (o.mode === independent) { fps = o._framerate; } } - this._framerate = fps; - - if (this.paused) { return; } - - // calculate how many frames to advance: - var t = (fps !== null && fps !== -1 && time !== null) ? time/(1000/fps) + this._t : 1; - var frames = t|0; - this._t = t-frames; // leftover time, save to add to next advance. - - while (frames--) { this._updateTimeline(this._rawPosition+1, false); } - }; - - /** - * MovieClip instances cannot be cloned. - * @method clone - **/ - p.clone = function() { - // TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex. - throw("MovieClip cannot be cloned."); - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[MovieClip (name="+ this.name +")]"; - }; - - -// private methods: - /** - * Docced in superclass. - **/ - p._updateState = function() { - if (this._rawPosition === -1 || this.mode !== MovieClip.INDEPENDENT) { this._updateTimeline(-1); } - }; - - /** - * @method _tick - * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. - * function. - * @protected - **/ - p._tick = function(evtObj) { - this.advance(evtObj&&evtObj.delta); - this.Container__tick(evtObj); - }; - - /** - * @method _goto - * @param {String|Number} positionOrLabel The animation name or frame number to go to. - * @protected - **/ - p._goto = function(positionOrLabel) { - var pos = this.timeline.resolve(positionOrLabel); - if (pos == null) { return; } - this._t = 0; - this._updateTimeline(pos, true); - }; - - /** - * @method _reset - * @private - **/ - p._reset = function() { - this._rawPosition = -1; - this._t = this.currentFrame = 0; - this.paused = false; - }; - - /** - * @method _updateTimeline - * @param {Boolean} jump Indicates whether this update is due to jumping (via gotoAndXX) to a new position. - * @protected - **/ - p._updateTimeline = function(rawPosition, jump) { - var synced = this.mode !== MovieClip.INDEPENDENT, tl = this.timeline; - if (synced) { rawPosition = this.startPosition + (this.mode===MovieClip.SINGLE_FRAME?0:this._synchOffset); } - if (rawPosition < 0) { rawPosition = 0; } - if (this._rawPosition === rawPosition && !synced) { return; } - this._rawPosition = rawPosition; - - // update timeline position, ignoring actions if this is a graphic. - tl.loop = this.loop; // TODO: should we maintain this on MovieClip, or just have it on timeline? - tl.setPosition(rawPosition, synced || !this.actionsEnabled, jump, this._bound_resolveState); - }; - - /** - * Renders position 0 without running actions or updating _rawPosition. - * Primarily used by Animate CC to build out the first frame in the constructor of MC symbols. - * NOTE: not tested when run after the MC advances past the first frame. - * @method _renderFirstFrame - * @protected - **/ - p._renderFirstFrame = function() { - var tl = this.timeline, pos = tl.rawPosition; - tl.setPosition(0, true, true, this._bound_resolveState); - tl.rawPosition = pos; - }; - - /** - * Runs via a callback after timeline property updates and before actions. - * @method _resolveState - * @protected - **/ - p._resolveState = function() { - var tl = this.timeline; - this.currentFrame = tl.position; - - for (var n in this._managed) { this._managed[n] = 1; } - - var tweens = tl.tweens; - for (var i=0, l=tweens.length; i=0; i--) { - var id = kids[i].id; - if (this._managed[id] === 1) { - this.removeChildAt(i); - delete(this._managed[id]); - } - } - }; - - /** - * @method _setState - * @param {Array} state - * @param {Number} offset - * @protected - **/ - p._setState = function(state, offset) { - if (!state) { return; } - for (var i=state.length-1;i>=0;i--) { - var o = state[i]; - var target = o.t; - var props = o.p; - for (var n in props) { target[n] = props[n]; } - this._addManagedChild(target, offset); - } - }; - - /** - * Adds a child to the timeline, and sets it up as a managed child. - * @method _addManagedChild - * @param {MovieClip} child The child MovieClip to manage - * @param {Number} offset - * @private - **/ - p._addManagedChild = function(child, offset) { - if (child._off) { return; } - this.addChildAt(child,0); - - if (child instanceof MovieClip) { - child._synchOffset = offset; - // TODO: this does not precisely match Adobe Flash/Animate, which loses track of the clip if it is renamed or removed from the timeline, which causes it to reset. - // TODO: should also reset when MovieClip loops, though that will be a bit tricky to detect. - if (child.mode === MovieClip.INDEPENDENT && child.autoReset && (!this._managed[child.id])) { child._reset(); } - } - this._managed[child.id] = 2; - }; - - /** - * @method _getBounds - * @param {Matrix2D} matrix - * @param {Boolean} ignoreTransform - * @return {Rectangle} - * @protected - **/ - p._getBounds = function(matrix, ignoreTransform) { - var bounds = this.DisplayObject_getBounds(); - if (!bounds) { - if (this.frameBounds) { bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]); } - } - if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); } - return this.Container__getBounds(matrix, ignoreTransform); - }; - - - createjs.MovieClip = createjs.promote(MovieClip, "Container"); - - - -// MovieClipPlugin for TweenJS: - /** - * This plugin works with TweenJS to prevent the startPosition - * property from tweening. - * @private - * @class MovieClipPlugin - * @constructor - **/ - function MovieClipPlugin() { - throw("MovieClipPlugin cannot be instantiated.") - } - - /** - * @property priority - * @type {Number} - * @static - * @readonly - **/ - MovieClipPlugin.priority = 100; // very high priority, should run first - - /** - * @property ID - * @type {String} - * @static - * @readonly - **/ - MovieClipPlugin.ID = "MovieClip"; - - /** - * @method install - * @static - **/ - MovieClipPlugin.install = function() { - createjs.Tween._installPlugin(MovieClipPlugin); - }; - - /** - * @method init - * @param {Tween} tween - * @param {String} prop - * @param {*} value - * @static - **/ - MovieClipPlugin.init = function(tween, prop, value) { - if (prop === "startPosition" && tween.target instanceof MovieClip) { tween._addPlugin(MovieClipPlugin); } - }; - - /** - * @method step - * @param {Tween} tween - * @param {TweenStep} step - * @param {Object} props - * @static - **/ - MovieClipPlugin.step = function(tween, step, props) {}; - - /** - * @method change - * @param {Tween} tween - * @param {TweenStep} step - * @param {*} value - * @param {Number} ratio - * @param {Object} end - * @return {*} - * @static - */ - MovieClipPlugin.change = function(tween, step, prop, value, ratio, end) { - if (prop === "startPosition") { return (ratio === 1 ? step.props[prop] : step.prev.props[prop]); } - }; - -}()); - -//############################################################################## -// SpriteSheetUtils.js -//############################################################################## - + createjs.ColorMatrix = ColorMatrix; +}()); + +//############################################################################## +// ColorMatrixFilter.js +//############################################################################## + this.createjs = this.createjs||{}; (function() { "use strict"; - - + + // constructor: /** - * The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s. - * A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For - * example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across - * by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated. - * @class SpriteSheetUtils - * @static + * Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the + * {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform, + * consider the {{#crossLink "ColorFilter"}}{{/crossLink}}. + * + *

      Example

      + * This example creates a red circle, inverts its hue, and then saturates it to brighten it up. + * + * var shape = new createjs.Shape().set({x:100,y:100}); + * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); + * + * var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100); + * shape.filters = [ + * new createjs.ColorMatrixFilter(matrix) + * ]; + * + * shape.cache(-50, -50, 100, 100); + * + * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. + * @class ColorMatrixFilter + * @constructor + * @extends Filter + * @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}} + * class. **/ - function SpriteSheetUtils() { - throw "SpriteSheetUtils cannot be instantiated"; + function ColorMatrixFilter(matrix) { + this.Filter_constructor(); + + // public properties: + /** + * A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}} + * @property matrix + * @type Array | ColorMatrix + **/ + this.matrix = matrix; + + this.FRAG_SHADER_BODY = ( + "uniform mat4 uColorMatrix;" + + "uniform vec4 uColorMatrixOffset;" + + + "void main(void) {" + + "vec4 color = texture2D(uSampler, vRenderCoord);" + + + "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];" + + + "gl_FragColor = newColor + uColorMatrixOffset;" + + "}" + ); } + var p = createjs.extend(ColorMatrixFilter, createjs.Filter); + // TODO: deprecated + // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. -// private static properties: + /** docced in super class **/ + p.shaderParamSetup = function(gl, stage, shaderProgram) { + var mat = this.matrix; + var colorMatrix = new Float32Array([ + mat[0],mat[1],mat[2],mat[3], + mat[5],mat[6],mat[7],mat[8], + mat[10],mat[11],mat[12],mat[13], + mat[15],mat[16],mat[17],mat[18] + ]); + + gl.uniformMatrix4fv( + gl.getUniformLocation(shaderProgram, "uColorMatrix"), + false, colorMatrix + ); + gl.uniform4f( + gl.getUniformLocation(shaderProgram, "uColorMatrixOffset"), + mat[4]/255, mat[9]/255, mat[14]/255, mat[19]/255 + ); + }; + +// public methods: + /** docced in super class **/ + p.toString = function() { + return "[ColorMatrixFilter]"; + }; + + /** docced in super class **/ + p.clone = function() { + return new ColorMatrixFilter(this.matrix); + }; + +// private methods: + /** docced in super class **/ + p._applyFilter = function(imageData) { + var data = imageData.data; + var l = data.length; + var r,g,b,a; + var mtx = this.matrix; + var m0 = mtx[0], m1 = mtx[1], m2 = mtx[2], m3 = mtx[3], m4 = mtx[4]; + var m5 = mtx[5], m6 = mtx[6], m7 = mtx[7], m8 = mtx[8], m9 = mtx[9]; + var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14]; + var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19]; + + for (var i=0; iExample + * + * var stage = new createjs.Stage("canvasId"); + * createjs.Touch.enable(stage); + * + * Note: It is important to disable Touch on a stage that you are no longer using: + * + * createjs.Touch.disable(stage); + * + * @class Touch + * @static + **/ + function Touch() { + throw "Touch cannot be instantiated"; + } + + +// public static methods: + /** + * Returns `true` if touch is supported in the current browser. + * @method isSupported + * @return {Boolean} Indicates whether touch is supported in the current browser. * @static - * @type HTMLCanvasElement | Object - * @protected - */ + **/ + Touch.isSupported = function() { + return !!(('ontouchstart' in window) // iOS & Android + || window.TouchEvent + || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10 + || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+ + }; + /** - * @property _workingContext + * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS + * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and + * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for + * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}} + * for more information. + * @method enable + * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on. + * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time. + * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be + * allowed when the user is interacting with the target canvas. + * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage. * @static - * @type CanvasRenderingContext2D + **/ + Touch.enable = function(stage, singleTouch, allowDefault) { + if (!stage || !stage.canvas || !Touch.isSupported()) { return false; } + if (stage.__touch) { return true; } + + // inject required properties on stage: + stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0}; + + // note that in the future we may need to disable the standard mouse event model before adding + // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though. + if ('ontouchstart' in window) { Touch._IOS_enable(stage); } + else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); } + return true; + }; + + /** + * Removes all listeners that were set up when calling `Touch.enable()` on a stage. + * @method disable + * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on. + * @static + **/ + Touch.disable = function(stage) { + if (!stage||!stage.__touch) { return; } + if ('ontouchstart' in window) { Touch._IOS_disable(stage); } + else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); } + + delete stage.__touch; + }; + + +// Private static methods: + /** + * @method _IOS_enable * @protected - */ - var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas")); - if (canvas.getContext) { - SpriteSheetUtils._workingCanvas = canvas; - SpriteSheetUtils._workingContext = canvas.getContext("2d"); - canvas.width = canvas.height = 1; - } + * @param {Stage} stage + * @static + **/ + Touch._IOS_enable = function(stage) { + var canvas = stage.canvas; + var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); }; + canvas.addEventListener("touchstart", f, false); + canvas.addEventListener("touchmove", f, false); + canvas.addEventListener("touchend", f, false); + canvas.addEventListener("touchcancel", f, false); + }; + /** + * @method _IOS_disable + * @protected + * @param {Stage} stage + * @static + **/ + Touch._IOS_disable = function(stage) { + var canvas = stage.canvas; + if (!canvas) { return; } + var f = stage.__touch.f; + canvas.removeEventListener("touchstart", f, false); + canvas.removeEventListener("touchmove", f, false); + canvas.removeEventListener("touchend", f, false); + canvas.removeEventListener("touchcancel", f, false); + }; -// public static methods: /** - * Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is - * to use a spritesheet frame as the source for a bitmap fill. - * - * WARNING: In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}} - * with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this - * method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}} - * property of {{#crossLink "Bitmap"}}{{/crossLink}}. - * - * The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas. - * @method extractFrame + * @method _IOS_handleEvent + * @param {Stage} stage + * @param {Object} e The event to handle + * @protected * @static - * @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from. - * @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation - * name is specified, only the first frame of the animation will be extracted. - * @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image. - */ - SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) { - if (isNaN(frameOrAnimation)) { - frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0]; + **/ + Touch._IOS_handleEvent = function(stage, e) { + if (!stage) { return; } + if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); } + var touches = e.changedTouches; + var type = e.type; + for (var i= 0,l=touches.length; i this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; } - var y=0, x=0; - var img = 0; - while (frames.length) { - var o = this._fillRow(frames, y, img, dataFrames, pad); - if (o.w > x) { x = o.w; } - y += o.h; - if (!o.h || !frames.length) { - var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); - canvas.width = this._getSize(x,this.maxWidth); - canvas.height = this._getSize(y,this.maxHeight); - this._data.images[img] = canvas; - if (!o.h) { - x=y=0; - img++; - } - } - } - }; - - /** - * @method _setupMovieClipFrame - * @protected - * @return {Number} The width & height of the row. - **/ - p._setupMovieClipFrame = function(source, data) { - var ae = source.actionsEnabled; - source.actionsEnabled = false; - source.gotoAndStop(data.i); - source.actionsEnabled = ae; - data.f&&data.f(source, data.d, data.i); - }; - - /** - * @method _getSize - * @protected - * @return {Number} The width & height of the row. - **/ - p._getSize = function(size,max) { - var pow = 4; - while (Math.pow(2,++pow) < size){} - return Math.min(max,Math.pow(2,pow)); - }; - - /** - * @method _fillRow - * @param {Array} frames - * @param {Number} y - * @param {HTMLImageElement} img - * @param {Object} dataFrames - * @param {Number} pad - * @protected - * @return {Number} The width & height of the row. - **/ - p._fillRow = function(frames, y, img, dataFrames, pad) { - var w = this.maxWidth; - var maxH = this.maxHeight; - y += pad; - var h = maxH-y; - var x = pad; - var height = 0; - for (var i=frames.length-1; i>=0; i--) { - var frame = frames[i]; - var sc = this._scale*frame.scale; - var rect = frame.sourceRect; - var source = frame.source; - var rx = Math.floor(sc*rect.x-pad); - var ry = Math.floor(sc*rect.y-pad); - var rh = Math.ceil(sc*rect.height+pad*2); - var rw = Math.ceil(sc*rect.width+pad*2); - if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; } - if (rh > h || x+rw > w) { continue; } - frame.img = img; - frame.rect = new createjs.Rectangle(x,y,rw,rh); - height = height || rh; - frames.splice(i,1); - dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)]; - x += rw; - } - return {w:x, h:height}; - }; - - /** - * @method _endBuild - * @protected - **/ - p._endBuild = function() { - this.spriteSheet = new createjs.SpriteSheet(this._data); - this._data = null; - this.progress = 1; - this.dispatchEvent("complete"); - }; - - /** - * @method _run - * @protected - **/ - p._run = function() { - var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50; - var t = (new Date()).getTime()+ts; - var complete = false; - while (t > (new Date()).getTime()) { - if (!this._drawNext()) { complete = true; break; } - } - if (complete) { - this._endBuild(); - } else { - var _this = this; - this._timerID = setTimeout(function() { _this._run(); }, 50-ts); - } - var p = this.progress = this._index/this._frames.length; - if (this.hasEventListener("progress")) { - var evt = new createjs.Event("progress"); - evt.progress = p; - this.dispatchEvent(evt); - } - }; - - /** - * @method _drawNext - * @protected - * @return Boolean Returns false if this is the last draw. - **/ - p._drawNext = function() { - var frame = this._frames[this._index]; - var sc = frame.scale*this._scale; - var rect = frame.rect; - var sourceRect = frame.sourceRect; - var canvas = this._data.images[frame.img]; - var ctx = canvas.getContext("2d"); - frame.funct&&frame.funct(frame.source, frame.data); - ctx.save(); - ctx.beginPath(); - ctx.rect(rect.x, rect.y, rect.width, rect.height); - ctx.clip(); - ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc)); - ctx.scale(sc,sc); - frame.source.draw(ctx); // display object will draw itself. - ctx.restore(); - return (++this._index) < this._frames.length; - }; - - - createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher"); -}()); - -//############################################################################## -// DOMElement.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * This class is still experimental, and more advanced use is likely to be buggy. Please report bugs. - * - * A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed - * within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is - * not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be - * drawn in front of or behind the canvas). - * - * The position of a DOMElement is relative to their parent node in the DOM. It is recommended that - * the DOM Object be added to a div that also contains the canvas so that they share the same position - * on the page. - * - * DOMElement is useful for positioning HTML elements over top of canvas content, and for elements - * that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML - * content. - * - *

      Mouse Interaction

      - * - * DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse - * events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to - * the htmlElement (note, this does not support EventDispatcher) - * - * var domElement = new createjs.DOMElement(htmlElement); - * domElement.htmlElement.onclick = function() { - * console.log("clicked"); - * } - * - * Important: This class needs to be notified it is about to be drawn, this will happen automatically - * if you call stage.update, calling stage.draw or disabling tickEnabled will miss important steps and it will render - * stale information. - * - * @class DOMElement - * @extends DisplayObject - * @constructor - * @param {HTMLElement} htmlElement A reference or id for the DOM element to manage. - */ - function DOMElement(htmlElement) { - this.DisplayObject_constructor(); - - if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); } - this.mouseEnabled = false; - - var style = htmlElement.style; - style.position = "absolute"; - style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%"; - - - // public properties: - /** - * The DOM object to manage. - * @property htmlElement - * @type HTMLElement - */ - this.htmlElement = htmlElement; - - - // private properties: - /** - * @property _oldMtx - * @type Matrix2D - * @protected - */ - this._oldProps = null; - - /** - * Used to track the object which this class attached listeners to, helps optimize listener attachment. - * @property _oldStage - * @type Stage - * @protected - */ - this._oldStage = null; - /** - * The event listener proxy triggered drawing draw for special circumstances. - * @property _drawAction - * @type function - * @protected - */ - this._drawAction = null; - } - var p = createjs.extend(DOMElement, createjs.DisplayObject); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// public methods: - /** - * Returns true or false indicating whether the display object would be visible if drawn to a canvas. - * This does not account for whether it would be visible within the boundaries of the stage. - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method isVisible - * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas - */ - p.isVisible = function() { - return this.htmlElement != null; - }; - - /** - * Draws the display object 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 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). - * @return {Boolean} - */ - p.draw = function(ctx, ignoreCache) { - // this relies on the _tick method because draw isn't called if the parent is not visible. - // the actual update happens in _handleDrawEnd - return true; - }; - - /** - * Not applicable to DOMElement. - * @method cache - */ - p.cache = function() {}; - - /** - * Not applicable to DOMElement. - * @method uncache - */ - p.uncache = function() {}; - - /** - * Not applicable to DOMElement. - * @method updateCache - */ - p.updateCache = function() {}; - - /** - * Not applicable to DOMElement. - * @method hitTest - */ - p.hitTest = function() {}; - - /** - * Not applicable to DOMElement. - * @method localToGlobal - */ - p.localToGlobal = function() {}; - - /** - * Not applicable to DOMElement. - * @method globalToLocal - */ - p.globalToLocal = function() {}; - - /** - * Not applicable to DOMElement. - * @method localToLocal - */ - p.localToLocal = function() {}; - - /** - * DOMElement cannot be cloned. Throws an error. - * @method clone - */ - p.clone = function() { - throw("DOMElement cannot be cloned.") - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - */ - p.toString = function() { - return "[DOMElement (name="+ this.name +")]"; - }; - - /** - * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances - * are not full EaselJS display objects and do not participate in EaselJS mouse events. - * @event click - */ - - /** - * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances - * are not full EaselJS display objects and do not participate in EaselJS mouse events. - * @event dblClick - */ - - /** - * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances - * are not full EaselJS display objects and do not participate in EaselJS mouse events. - * @event mousedown - */ - - /** - * The HTMLElement can listen for the mouseover event, not the DOMElement instance. - * Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events. - * @event mouseover - */ - - /** - * Not applicable to DOMElement. - * @event tick - */ - - -// private methods: - /** - * @method _tick - * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs. - * function. - * @protected - */ - p._tick = function(evtObj) { - var stage = this.stage; - if(stage && stage !== this._oldStage) { - this._drawAction && stage.off("drawend", this._drawAction); - this._drawAction = stage.on("drawend", this._handleDrawEnd, this); - this._oldStage = stage; - } - this.DisplayObject__tick(evtObj); - }; - - /** - * @method _handleDrawEnd - * @param {Event} evt - * @protected - */ - p._handleDrawEnd = function(evt) { - var o = this.htmlElement; - if (!o) { return; } - var style = o.style; - - var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix; - - var visibility = props.visible ? "visible" : "hidden"; - if (visibility != style.visibility) { style.visibility = visibility; } - if (!props.visible) { return; } - - var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix; - var n = 10000; // precision - - if (!oldMtx || !oldMtx.equals(mtx)) { - var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0); - style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")"; - style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)"; - if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, null); } - oldProps.matrix.copy(mtx); - } - - if (oldProps.alpha != props.alpha) { - style.opacity = ""+(props.alpha*n|0)/n; - oldProps.alpha = props.alpha; - } - }; - - - createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject"); -}()); - -//############################################################################## -// Filter.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// 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. - * - *

      Example

      - * - * myInstance.filters = [ - * new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0), - * new createjs.BlurFilter(5, 5, 10) - * ]; - * myInstance.cache(0,0, 100, 100); - * - * Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the - * 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. - * - *

      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}}
      • - *
      - * - * @class Filter - * @constructor - **/ - function Filter() { - /** - * A flag stating that this filter uses a context draw mode and cannot be batched into imageData processing. - * @property usesContext - * @type {boolean} - * @default false - */ - this.usesContext = false; - - /** - * Another filter that is required to act as part of this filter and created and managed under the hood. - * @private - * @property _multiPass - * @type {Filter} - * @default null - */ - this._multiPass = null; - - /** - * 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 - */ - this.VTX_SHADER_BODY = null; - - /** - * 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 - */ - this.FRAG_SHADER_BODY = null; - } - var p = Filter.prototype; - -// 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. - * @method getBounds - * @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned. - * @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter. - **/ - p.getBounds = function(rect) { - return rect; - }; - - /** - * Assign any unique uniforms or other setup functionality here. - * @method shaderParamSetup - * @virtual - * @param {WebGLContext} gl The context associated with the stage performing the render. - * @param {StageGL} stage The stage instance that will be rendering. - * @param {ShaderProgram} shaderProgram The compiled shader that is going to be used to perform the render. - */ - p.shaderParamSetup = function(gl, stage, shaderProgram) {}; - - /** - * Applies the filter to the specified context. - * @method applyFilter - * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source. - * @param {Number} x The x position to use for the source rect. - * @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. - * @return {Boolean} If the filter was applied successfully. - **/ - p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) { - // 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); - return true; - } - return false; - }; - - /** - * Returns a string representation of this object. - * @method toString - * @return {String} a string representation of the instance. - **/ - p.toString = function() { - return "[Filter]"; - }; - - /** - * Returns a clone of this Filter instance. - * @method clone - * @return {Filter} A clone of the current Filter instance. - **/ - p.clone = function() { - return new Filter(); - }; - -// private methods: - /** - * @method _applyFilter - * @param {ImageData} imageData Target ImageData instance. - * @return {Boolean} - **/ - p._applyFilter = function(imageData) { return true; }; - - - createjs.Filter = Filter; -}()); - -//############################################################################## -// BitmapCache.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * The BitmapCache is an internal representation of all the cache properties and logic required in order to "cache" - * an object. This information and functionality used to be located on a {{#crossLink "DisplayObject/cache"}}{{/crossLink}} - * method in {{#crossLink "DisplayObject"}}{{/crossLink}}, but was moved to its own class. - * - * Caching in this context is purely visual, and will render the DisplayObject out into an image to be used instead - * of the object. The actual cache itself is still stored on the target with the {{#crossLink "DisplayObject/cacheCanvas:property"}}{{/crossLink}}. - * Working with a singular image like a {{#crossLink "Bitmap"}}{{/crossLink}} there is little benefit to performing - * a cache as it is already a single image. Caching is best done on containers containing multiple complex parts that - * do not move often, so that rendering the image instead will improve overall rendering speed. A cached object will - * not visually update until explicitly told to do so with a call to update, much like a Stage. If a cache is being - * updated every frame it is likely not improving rendering performance. Cache are best used when updates will be sparse. - * - * Caching is also a co-requisite for applying filters to prevent expensive filters running constantly without need, - * and to physically enable some effects. The BitmapCache is also responsible for applying filters to objects and - * reads each {{#crossLink "Filter"}}{{/crossLink}} due to this relationship. Real-time Filters are not recommended - * performance wise when dealing with a Context2D canvas. For best performance and to still allow for some visual - * effects use a compositeOperation when possible. - * @class BitmapCache - * @constructor - **/ - function BitmapCache() { - - // public: - /** - * Width of the cache relative to the target object. - * @property width - * @protected - * @type {Number} - * @default undefined - **/ - this.width = undefined; - - /** - * Height of the cache relative to the target object. - * @property height - * @protected - * @type {Number} - * @default undefined - * @todo Should the width and height be protected? - **/ - this.height = undefined; - - /** - * Horizontal position of the cache relative to the target's origin. - * @property x - * @protected - * @type {Number} - * @default undefined - **/ - this.x = undefined; - - /** - * Vertical position of the cache relative to target's origin. - * @property y - * @protected - * @type {Number} - * @default undefined - **/ - this.y = undefined; - - /** - * The internal scale of the cache image, does not affects display size. This is useful to both increase and - * decrease render quality. Objects with increased scales are more likely to look good when scaled up or rotated. - * Objects with decreased scales can save on rendering performance. - * @property scale - * @protected - * @type {Number} - * @default 1 - **/ - this.scale = 1; - - /** - * The x offset used for drawing into the cache itself, accounts for both transforms applied. - * @property offX - * @protected - * @type {Number} - * @default 0 - **/ - this.offX = 0; - - /** - * The y offset used for drawing into the cache itself, accounts for both transforms applied. - * @property offY - * @protected - * @type {Number} - * @default 0 - **/ - this.offY = 0; - - /** - * Track how many times the cache has been updated, mostly used for preventing duplicate cacheURLs. - * This can be useful to see if a cache has been updated. - * @property cacheID - * @type {Number} - * @default 0 - **/ - this.cacheID = 0; - - // protected: - /** - * The relative offset of the filter's x position, used for drawing the cache onto its container. - * Re-calculated every update call before drawing. - * @property _filterOffY - * @protected - * @type {Number} - * @default 0 - **/ - this._filterOffX = 0; - - /** - * The relative offset of the filter's y position, used for drawing the cache onto its container. - * Re-calculated every update call before drawing. - * @property _filterOffY - * @protected - * @type {Number} - * @default 0 - **/ - this._filterOffY = 0; - - /** - * The cacheID when a DataURL was requested. - * @property _cacheDataURLID - * @protected - * @type {Number} - * @default 0 - **/ - this._cacheDataURLID = 0; - - /** - * The cache's DataURL, generated on-demand using the getter. - * @property _cacheDataURL - * @protected - * @type {String} - * @default null - **/ - this._cacheDataURL = null; - - /** - * Internal tracking of final bounding width, approximately width*scale; however, filters can complicate the actual value. - * @property _drawWidth - * @protected - * @type {Number} - * @default 0 - **/ - this._drawWidth = 0; - - /** - * Internal tracking of final bounding height, approximately height*scale; however, filters can complicate the actual value. - * @property _drawHeight - * @protected - * @type {Number} - * @default 0 - **/ - this._drawHeight = 0; - } - var p = BitmapCache.prototype; - - /** - * Returns the bounds that surround all applied filters, relies on each filter to describe how it changes bounds. - * @method getFilterBounds - * @param {DisplayObject} target The object to check the filter bounds for. - * @param {Rectangle} [output=null] Optional parameter, if provided then calculated bounds will be applied to that object. - * @return {Rectangle} bounds object representing the bounds with filters. - * @static - **/ - BitmapCache.getFilterBounds = function(target, output) { - if(!output){ output = new createjs.Rectangle(); } - var filters = target.filters; - var filterCount = filters && filters.length; - if (!!filterCount <= 0) { return output; } - - for(var i=0; iWebGL cache with a 2D context - * - * var stage = new createjs.Stage(); - * var bmp = new createjs.Bitmap(src); - * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "new"}); // no StageGL to use, so make one - * - * var shape = new createjs.Shape(); - * shape.graphics.clear().fill("red").drawRect(0,0,20,20); - * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache - * - *

      WebGL cache with a WebGL context

      - * - * var stageGL = new createjs.StageGL(); - * var bmp = new createjs.Bitmap(src); - * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "stage"}); // use our StageGL to cache - * - * var shape = new createjs.Shape(); - * shape.graphics.clear().fill("red").drawRect(0,0,20,20); - * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache - * - * You may wish to create your own StageGL instance to control factors like clear color, transparency, AA, and - * others. If you do, pass a new instance in instead of "true", the library will automatically set the - * {{#crossLink "StageGL/isCacheControlled"}}{{/crossLink}} to true on your instance. This will trigger it to behave - * correctly, and not assume your main context is WebGL. - * - * @public - * @method BitmapCache.cache - * @param {Number} x The x coordinate origin for the cache region. - * @param {Number} y The y coordinate origin for the cache region. - * @param {Number} width The width of the cache region. - * @param {Number} height The height of the cache region. - * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape - * using myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and - * rotate cached elements with greater fidelity. Default is 1. - * @param {Object} [options=undefined] Specify additional parameters for the cache logic - * @param {undefined|"new"|"stage"|StageGL} [options.useGL=undefined] Select whether to use context 2D, or WebGL rendering, and - * whether to make a new stage instance or use an existing one. See above for extensive details on use. - * @for BitmapCache - */ - p.define = function(target, x, y, width, height, scale, options) { - if(!target){ throw "No symbol to cache"; } - this._options = options; - this.target = target; - - this.width = width >= 1 ? width : 1; - this.height = height >= 1 ? height : 1; - this.x = x || 0; - this.y = y || 0; - this.scale = scale || 1; - - this.update(); - }; - - /** - * Directly called via {{#crossLink "DisplayObject/updateCache:method"}}{{/crossLink}}, but also internally. This - * has the dual responsibility of making sure the surface is ready to be drawn to, and performing the draw. For - * full details of each behaviour, check the protected functions {{#crossLink "BitmapCache/_updateSurface"}}{{/crossLink}} - * and {{#crossLink "BitmapCache/_drawToCache"}}{{/crossLink}} respectively. - * @method update - * @param {String} [compositeOperation=null] The DisplayObject this cache is linked to. - **/ - p.update = function(compositeOperation) { - if(!this.target) { throw "define() must be called before update()"; } - - var filterBounds = BitmapCache.getFilterBounds(this.target); - var surface = this.target.cacheCanvas; - - this._drawWidth = Math.ceil(this.width*this.scale) + filterBounds.width; - this._drawHeight = Math.ceil(this.height*this.scale) + filterBounds.height; - - if(!surface || this._drawWidth != surface.width || this._drawHeight != surface.height) { - this._updateSurface(); - } - - this._filterOffX = filterBounds.x; - this._filterOffY = filterBounds.y; - this.offX = this.x*this.scale + this._filterOffX; - this.offY = this.y*this.scale + this._filterOffY; - - this._drawToCache(compositeOperation); - - this.cacheID = this.cacheID?this.cacheID+1:1; - }; - - /** - * Reset and release all the properties and memory associated with this cache. - * @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); } - } - // set the context to none and let the garbage collector get the rest when the canvas itself gets removed - this._webGLCache = false; - } else { - var stage = this.target.stage; - if (stage instanceof createjs.StageGL){ - stage.releaseTexture(this.target.cacheCanvas); - } - } - - this.target = this.target.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; - }; - - /** - * Returns a data URL for the cache, or `null` if this display object is not cached. - * Uses {{#crossLink "BitmapCache/cacheID:property"}}{{/crossLink}} to ensure a new data URL is not generated if the - * cache has not changed. - * @method getCacheDataURL - * @return {String} The image data url for the cache. - **/ - p.getCacheDataURL = function() { - var cacheCanvas = this.target && this.target.cacheCanvas; - if (!cacheCanvas) { return null; } - if (this.cacheID != this._cacheDataURLID) { - this._cacheDataURLID = this.cacheID; - this._cacheDataURL = cacheCanvas.toDataURL?cacheCanvas.toDataURL():null; // incase function is - } - return this._cacheDataURL; - }; - - /** - * Use context2D drawing commands to display the cache canvas being used. - * @method draw - * @param {CanvasRenderingContext2D} ctx The context to draw into. - * @return {Boolean} Whether the draw was handled successfully. - **/ - p.draw = function(ctx) { - if(!this.target) { return false; } - ctx.drawImage(this.target.cacheCanvas, - this.x + (this._filterOffX/this.scale), this.y + (this._filterOffY/this.scale), - this._drawWidth/this.scale, this._drawHeight/this.scale - ); - return true; - }; - -// private methods: - /** - * Create or resize the invisible canvas/surface that is needed for the display object(s) to draw to, - * and in turn be used in their stead when drawing. The surface is resized to the size defined - * by the width and height, factoring in scaling and filters. Adjust them to adjust the output size. - * @method _updateSurface - * @protected - **/ - p._updateSurface = function() { - if (!this._options || !this._options.useGL) { - var surface = this.target.cacheCanvas; - - // create it if it's missing - if(!surface) { - surface = this.target.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); - } - - // now size it - surface.width = this._drawWidth; - surface.height = this._drawHeight; - return; - } - - // create it if it's missing - if (!this._webGLCache) { - if (this._options.useGL === "stage") { - if(!(this.target.stage && this.target.stage.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."; - throw error; - } - this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks - this._webGLCache = this.target.stage; - - } 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 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 { - throw "Invalid option provided to useGL, expected ['stage', 'new', StageGL, undefined], got "+ this._options.useGL; - } - } - - // now size render surfaces - var surface = this.target.cacheCanvas; - var stageGL = this._webGLCache; - - // if we have a dedicated stage we've gotta size it - if (stageGL.isCacheControlled) { - 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); - } - } - }; - - /** - * Perform the cache draw out for context 2D now that the setup properties have been performed. - * @method _drawToCache - * @protected - **/ - p._drawToCache = function(compositeOperation) { - var surface = this.target.cacheCanvas; - var target = this.target; - var webGL = this._webGLCache; - - if (webGL){ - //TODO: auto split blur into an x/y pass - 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; - - surface.width = this._drawWidth; - surface.height = this._drawHeight; - } else { - var ctx = surface.getContext("2d"); - - if (!compositeOperation) { - ctx.clearRect(0, 0, this._drawWidth+1, this._drawHeight+1); - } - - ctx.save(); - ctx.globalCompositeOperation = compositeOperation; - ctx.setTransform(this.scale,0,0,this.scale, -this._filterOffX,-this._filterOffY); - ctx.translate(-this.x, -this.y); - target.draw(ctx, true); - ctx.restore(); - - - if (target.filters && target.filters.length) { - this._applyFilters(ctx); - } - } - surface._invalid = true; - }; - - /** - * Work through every filter and apply its individual visual transformation. - * @method _applyFilters - * @protected - **/ - p._applyFilters = function(ctx) { - var filters = this.target.filters; - - var w = this._drawWidth; - var h = this._drawHeight; - - var data; - - 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) { - ctx.putImageData(data, 0,0); - data = null; - } - filter.applyFilter(ctx, 0,0, w,h); - } else { - if(!data) { - data = ctx.getImageData(0,0, w,h); - } - filter._applyFilter(data); - } - - // work through the multipass if it's there, otherwise move on - filter = filter._multiPass !== null ? filter._multiPass : filters[++i]; - } while (filter); - - //done - if(data) { - ctx.putImageData(data, 0,0); - } - }; - - createjs.BitmapCache = BitmapCache; -}()); - -//############################################################################## -// BlurFilter.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Applies a box blur to DisplayObjects in context 2D and a Gaussian blur in webgl. Note that this filter is fairly - * intensive, particularly if the quality is set higher than 1. - * - *

      Example

      - * This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}} - * method to account for the spread that the blur causes. - * - * var shape = new createjs.Shape().set({x:100,y:100}); - * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); - * - * var blurFilter = new createjs.BlurFilter(5, 5, 1); - * shape.filters = [blurFilter]; - * var bounds = blurFilter.getBounds(); - * - * shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. - * @class BlurFilter - * @extends Filter - * @constructor - * @param {Number} [blurX=0] The horizontal blur radius in pixels. - * @param {Number} [blurY=0] The vertical blur radius in pixels. - * @param {Number} [quality=1] The number of blur iterations. - **/ - function BlurFilter( blurX, blurY, quality) { - this.Filter_constructor(); - - // public properties: - /** - * Horizontal blur radius in pixels - * @property blurX - * @default 0 - * @type Number - **/ - this._blurX = blurX; - this._blurXTable = []; - this._lastBlurX = null; - - /** - * Vertical blur radius in pixels - * @property blurY - * @default 0 - * @type Number - **/ - this._blurY = blurY; - this._blurYTable = []; - this._lastBlurY = null; - - /** - * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a - * smoother blur, but take twice as long to run. - * @property quality - * @default 1 - * @type Number - **/ - this._quality; - this._lastQuality = null; - - /** - * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}} - */ - this.FRAG_SHADER_TEMPLATE = ( - "uniform float xWeight[{{blurX}}];" + - "uniform float yWeight[{{blurY}}];" + - "uniform vec2 textureOffset;" + - "void main(void) {" + - "vec4 color = vec4(0.0);" + - - "float xAdj = ({{blurX}}.0-1.0)/2.0;" + - "float yAdj = ({{blurY}}.0-1.0)/2.0;" + - "vec2 sampleOffset;" + - - "for(int i=0; i<{{blurX}}; i++) {" + - "for(int j=0; j<{{blurY}}; j++) {" + - "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" + - "color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" + - "}" + - "}" + - - "gl_FragColor = color.rgba;" + - "}" - ); - - // update the filter using the setters - if(isNaN(quality) || quality < 1){ quality = 1; } - this.setQuality(quality|0); - } - var p = createjs.extend(BlurFilter, createjs.Filter); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - p.getBlurX = function() { return this._blurX; }; - p.getBlurY = function() { return this._blurY; }; - p.setBlurX = function(value) { - if(isNaN(value) || value < 0){ value = 0; } - this._blurX = value; - }; - p.setBlurY = function(value) { - if(isNaN(value) || value < 0){ value = 0; } - this._blurY = value; - }; - p.getQuality = function() { return this._quality; }; - p.setQuality = function(value) { - if(isNaN(value) || value < 0){ value = 0; } - this._quality = value | 0; - }; - p._getShader = function() { - var xChange = this._lastBlurX !== this._blurX; - var yChange = this._lastBlurY !== this._blurY; - var qChange = this._lastQuality !== this._quality; - if(xChange || yChange || qChange) { - if(xChange || qChange) { this._blurXTable = this._getTable(this._blurX * this._quality); } - if(yChange || qChange) { this._blurYTable = this._getTable(this._blurY * this._quality); } - this._updateShader(); - this._lastBlurX = this._blurX; - this._lastBlurY = this._blurY; - this._lastQuality = this._quality; - return undefined; // force a rebuild - } - return this._compiledShader; - }; - p._setShader = function() { this._compiledShader; }; - - try { - Object.defineProperties(p, { - blurX: { get: p.getBlurX, set: p.setBlurX }, - blurY: { get: p.getBlurY, set: p.setBlurY }, - quality: { get: p.getQuality, set: p.setQuality }, - _builtShader: { get: p._getShader, set: p._setShader} - }); - } catch (e) { console.log(e); } - - /** - * Internal lookup function to create gaussian distribution. - * @method _getTable - * @param {Number} spread How many steps in the curve. - * @return {Array} An array with Math.ceil(spread*2) entries with appropriately distributed weights. - */ - p._getTable = function(spread) { - var EDGE = 4.2; - if(spread<=1) { return [1]; } - - var result = []; - var count = Math.ceil(spread*2); - count += (count%2)?0:1; - var adjust = (count/2)|0; - for(var i = -adjust; i<=adjust; i++) { - var x = (i/adjust)*EDGE; - result.push(1/Math.sqrt(2*Math.PI) * Math.pow(Math.E, -(Math.pow(x,2)/4))); - } - var factor = result.reduce(function(a, b) { return a + b; }); - return result.map(function(currentValue, index, array) { return currentValue/factor; }); - }; - - /** - * Internal update function to create shader properties. - * @method _updateShader - */ - p._updateShader = function() { - if(this._blurX === undefined || this._blurY === undefined){ return; } - var result = this.FRAG_SHADER_TEMPLATE; - result = result.replace(/\{\{blurX\}\}/g, (this._blurXTable.length).toFixed(0)); - result = result.replace(/\{\{blurY\}\}/g, (this._blurYTable.length).toFixed(0)); - this.FRAG_SHADER_BODY = result; - }; - - /** docced in super class **/ - p.shaderParamSetup = function(gl, stage, shaderProgram) { - // load the normalized gaussian weight tables - gl.uniform1fv( - gl.getUniformLocation(shaderProgram, "xWeight"), - this._blurXTable - ); - gl.uniform1fv( - gl.getUniformLocation(shaderProgram, "yWeight"), - this._blurYTable - ); - - // what is the size of a single pixel in -1, 1 (webGL) space - gl.uniform2f( - gl.getUniformLocation(shaderProgram, "textureOffset"), - 2/(stage._viewportWidth*this._quality), 2/(stage._viewportHeight*this._quality) - ); - }; - -// constants: - /** - * Array of multiply values for blur calculations. - * @property MUL_TABLE - * @type Array - * @protected - * @static - **/ - BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1]; - - /** - * Array of shift values for blur calculations. - * @property SHG_TABLE - * @type Array - * @protected - * @static - **/ - BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9]; - -// public methods: - /** docced in super class **/ - p.getBounds = function (rect) { - var x = this.blurX|0, y = this.blurY| 0; - if(x <= 0 && y <= 0) { return rect; } - var q = Math.pow(this.quality, 0.2); - return (rect || new createjs.Rectangle()).pad(y*q+1,x*q+1,y*q+1,x*q+1); - }; - - /** docced in super class **/ - p.clone = function() { - return new BlurFilter(this.blurX, this.blurY, this.quality); - }; - - /** docced in super class **/ - p.toString = function() { - return "[BlurFilter]"; - }; - - -// private methods: - - /** docced in super class **/ - p._applyFilter = function (imageData) { - var radiusX = this._blurX >> 1; - if (isNaN(radiusX) || radiusX < 0) return false; - var radiusY = this._blurY >> 1; - if (isNaN(radiusY) || radiusY < 0) return false; - if (radiusX == 0 && radiusY == 0) return false; - - var iterations = this.quality; - if (isNaN(iterations) || iterations < 1) iterations = 1; - iterations |= 0; - if (iterations > 3) iterations = 3; - if (iterations < 1) iterations = 1; - - var px = imageData.data; - var x=0, y=0, i=0, p=0, yp=0, yi=0, yw=0, r=0, g=0, b=0, a=0, pr=0, pg=0, pb=0, pa=0; - - var divx = (radiusX + radiusX + 1) | 0; - var divy = (radiusY + radiusY + 1) | 0; - var w = imageData.width | 0; - var h = imageData.height | 0; - - var w1 = (w - 1) | 0; - var h1 = (h - 1) | 0; - var rxp1 = (radiusX + 1) | 0; - var ryp1 = (radiusY + 1) | 0; - - var ssx = {r:0,b:0,g:0,a:0}; - var sx = ssx; - for ( i = 1; i < divx; i++ ) - { - sx = sx.n = {r:0,b:0,g:0,a:0}; - } - sx.n = ssx; - - var ssy = {r:0,b:0,g:0,a:0}; - var sy = ssy; - for ( i = 1; i < divy; i++ ) - { - sy = sy.n = {r:0,b:0,g:0,a:0}; - } - sy.n = ssy; - - var si = null; - - - var mtx = BlurFilter.MUL_TABLE[radiusX] | 0; - var stx = BlurFilter.SHG_TABLE[radiusX] | 0; - var mty = BlurFilter.MUL_TABLE[radiusY] | 0; - var sty = BlurFilter.SHG_TABLE[radiusY] | 0; - - while (iterations-- > 0) { - - yw = yi = 0; - var ms = mtx; - var ss = stx; - for (y = h; --y > -1;) { - r = rxp1 * (pr = px[(yi) | 0]); - g = rxp1 * (pg = px[(yi + 1) | 0]); - b = rxp1 * (pb = px[(yi + 2) | 0]); - a = rxp1 * (pa = px[(yi + 3) | 0]); - - sx = ssx; - - for( i = rxp1; --i > -1; ) - { - sx.r = pr; - sx.g = pg; - sx.b = pb; - sx.a = pa; - sx = sx.n; - } - - for( i = 1; i < rxp1; i++ ) - { - p = (yi + ((w1 < i ? w1 : i) << 2)) | 0; - r += ( sx.r = px[p]); - g += ( sx.g = px[p+1]); - b += ( sx.b = px[p+2]); - a += ( sx.a = px[p+3]); - - sx = sx.n; - } - - si = ssx; - for ( x = 0; x < w; x++ ) - { - px[yi++] = (r * ms) >>> ss; - px[yi++] = (g * ms) >>> ss; - px[yi++] = (b * ms) >>> ss; - px[yi++] = (a * ms) >>> ss; - - p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2); - - r -= si.r - ( si.r = px[p]); - g -= si.g - ( si.g = px[p+1]); - b -= si.b - ( si.b = px[p+2]); - a -= si.a - ( si.a = px[p+3]); - - si = si.n; - - } - yw += w; - } - - ms = mty; - ss = sty; - for (x = 0; x < w; x++) { - yi = (x << 2) | 0; - - r = (ryp1 * (pr = px[yi])) | 0; - g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0; - b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0; - a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0; - - sy = ssy; - for( i = 0; i < ryp1; i++ ) - { - sy.r = pr; - sy.g = pg; - sy.b = pb; - sy.a = pa; - sy = sy.n; - } - - yp = w; - - for( i = 1; i <= radiusY; i++ ) - { - yi = ( yp + x ) << 2; - - r += ( sy.r = px[yi]); - g += ( sy.g = px[yi+1]); - b += ( sy.b = px[yi+2]); - a += ( sy.a = px[yi+3]); - - sy = sy.n; - - if( i < h1 ) - { - yp += w; - } - } - - yi = x; - si = ssy; - if ( iterations > 0 ) - { - for ( y = 0; y < h; y++ ) - { - p = yi << 2; - px[p+3] = pa =(a * ms) >>> ss; - if ( pa > 0 ) - { - px[p] = ((r * ms) >>> ss ); - px[p+1] = ((g * ms) >>> ss ); - px[p+2] = ((b * ms) >>> ss ); - } else { - px[p] = px[p+1] = px[p+2] = 0 - } - - p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2; - - r -= si.r - ( si.r = px[p]); - g -= si.g - ( si.g = px[p+1]); - b -= si.b - ( si.b = px[p+2]); - a -= si.a - ( si.a = px[p+3]); - - si = si.n; - - yi += w; - } - } else { - for ( y = 0; y < h; y++ ) - { - p = yi << 2; - px[p+3] = pa =(a * ms) >>> ss; - if ( pa > 0 ) - { - pa = 255 / pa; - px[p] = ((r * ms) >>> ss ) * pa; - px[p+1] = ((g * ms) >>> ss ) * pa; - px[p+2] = ((b * ms) >>> ss ) * pa; - } else { - px[p] = px[p+1] = px[p+2] = 0 - } - - p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2; - - r -= si.r - ( si.r = px[p]); - g -= si.g - ( si.g = px[p+1]); - b -= si.b - ( si.b = px[p+2]); - a -= si.a - ( si.a = px[p+3]); - - si = si.n; - - yi += w; - } - } - } - - } - return true; - }; - - createjs.BlurFilter = createjs.promote(BlurFilter, "Filter"); -}()); - -//############################################################################## -// AlphaMapFilter.js -//############################################################################## - -this.createjs = this.createjs || {}; - -(function () { - "use strict"; - - -// constructor: - /** - * Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will - * be copied from the red channel of the map, and the RGB channels will be copied from the target. - * - * Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much - * better performance. - * - *

      Example

      - * This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image. - * - * var box = new createjs.Shape(); - * box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100) - * box.graphics.drawRect(0, 0, 100, 100); - * box.cache(0, 0, 100, 100); - * - * var bmp = new createjs.Bitmap("path/to/image.jpg"); - * bmp.filters = [ - * new createjs.AlphaMapFilter(box.cacheCanvas) - * ]; - * bmp.cache(0, 0, 100, 100); - * stage.addChild(bmp); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters. - * @class AlphaMapFilter - * @extends Filter - * @constructor - * @param {HTMLImageElement|HTMLCanvasElement} 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(); - - // public properties: - /** - * The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same - * dimensions as the target. - * @property alphaMap - * @type HTMLImageElement|HTMLCanvasElement - **/ - this.alphaMap = alphaMap; - - - // private properties: - /** - * @property _alphaMap - * @protected - * @type HTMLImageElement|HTMLCanvasElement - **/ - this._alphaMap = null; - - /** - * @property _mapData - * @protected - * @type Uint8ClampedArray - **/ - this._mapData = null; - this._mapTexture = null; - - this.FRAG_SHADER_BODY = ( - "uniform sampler2D uAlphaSampler;"+ - - "void main(void) {" + - "vec4 color = texture2D(uSampler, vRenderCoord);" + - "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)));" + - "}" - ); - } - var p = createjs.extend(AlphaMapFilter, createjs.Filter); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - /** 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); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap); - - gl.uniform1i( - gl.getUniformLocation(shaderProgram, "uAlphaSampler"), - 1 - ); - }; - -// public methods: - /** docced in super class **/ - p.clone = function () { - var o = new AlphaMapFilter(this.alphaMap); - o._alphaMap = this._alphaMap; - o._mapData = this._mapData; - return o; - }; - - /** docced in super class **/ - p.toString = function () { - return "[AlphaMapFilter]"; - }; - - -// private methods: - /** docced in super class **/ - p._applyFilter = function (imageData) { - if (!this.alphaMap) { return true; } - 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; iIMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly. - * - *

      Example

      - * This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image. - * - * var box = new createjs.Shape(); - * box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100) - * box.graphics.drawRect(0, 0, 100, 100); - * box.cache(0, 0, 100, 100); - * - * var bmp = new createjs.Bitmap("path/to/image.jpg"); - * bmp.filters = [ - * new createjs.AlphaMaskFilter(box.cacheCanvas) - * ]; - * bmp.cache(0, 0, 100, 100); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters. - * @class AlphaMaskFilter - * @extends Filter - * @constructor - * @param {HTMLImageElement|HTMLCanvasElement} mask - **/ - function AlphaMaskFilter(mask) { - this.Filter_constructor(); - - // public properties: - /** - * The image (or canvas) to use as the mask. - * @property mask - * @type HTMLImageElement|HTMLCanvasElement - **/ - this.mask = mask; - - /** docced in super class **/ - this.usesContext = true; - - this.FRAG_SHADER_BODY = ( - "uniform sampler2D uAlphaSampler;"+ - - "void main(void) {" + - "vec4 color = texture2D(uSampler, vRenderCoord);" + - "vec4 alphaMap = texture2D(uAlphaSampler, vTextureCoord);" + - - "gl_FragColor = vec4(color.rgb, color.a * alphaMap.a);" + - "}" - ); - } - var p = createjs.extend(AlphaMaskFilter, createjs.Filter); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - /** 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); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.mask); - - gl.uniform1i( - gl.getUniformLocation(shaderProgram, "uAlphaSampler"), - 1 - ); - }; - -// public methods: - /** - * Applies the filter to the specified context. - * - * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters - * correctly. - * @method applyFilter - * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source. - * @param {Number} x The x position to use for the source rect. - * @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] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx. - * @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x. - * @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y. - * @return {Boolean} If the filter was applied successfully. - **/ - p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) { - if (!this.mask) { return true; } - targetCtx = targetCtx || ctx; - if (targetX == null) { targetX = x; } - if (targetY == null) { targetY = y; } - - targetCtx.save(); - if (ctx != targetCtx) { - // TODO: support targetCtx and targetX/Y - // clearRect, then draw the ctx in? - return false; - } - - targetCtx.globalCompositeOperation = "destination-in"; - targetCtx.drawImage(this.mask, targetX, targetY); - targetCtx.restore(); - return true; - }; - - /** docced in super class **/ - p.clone = function () { - return new AlphaMaskFilter(this.mask); - }; - - /** docced in super class **/ - p.toString = function () { - return "[AlphaMaskFilter]"; - }; - - - createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter"); -}()); - -//############################################################################## -// ColorFilter.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Applies a color transform to DisplayObjects. - * - *

      Example

      - * This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels - * to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel. - * - * var shape = new createjs.Shape().set({x:100,y:100}); - * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); - * - * shape.filters = [ - * new createjs.ColorFilter(0,0,0,1, 0,0,255,0) - * ]; - * shape.cache(-50, -50, 100, 100); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. - * @class ColorFilter - * @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1. - * @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1. - * @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1. - * @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1. - * @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range - * between -255 and 255. - * @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range - * between -255 and 255. - * @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range - * between -255 and 255. - * @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range - * between -255 and 255. - * @constructor - * @extends Filter - **/ - function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) { - this.Filter_constructor(); - - // public properties: - /** - * Red channel multiplier. - * @property redMultiplier - * @type Number - **/ - this.redMultiplier = redMultiplier != null ? redMultiplier : 1; - - /** - * Green channel multiplier. - * @property greenMultiplier - * @type Number - **/ - this.greenMultiplier = greenMultiplier != null ? greenMultiplier : 1; - - /** - * Blue channel multiplier. - * @property blueMultiplier - * @type Number - **/ - this.blueMultiplier = blueMultiplier != null ? blueMultiplier : 1; - - /** - * Alpha channel multiplier. - * @property alphaMultiplier - * @type Number - **/ - this.alphaMultiplier = alphaMultiplier != null ? alphaMultiplier : 1; - - /** - * Red channel offset (added to value). - * @property redOffset - * @type Number - **/ - this.redOffset = redOffset || 0; - - /** - * Green channel offset (added to value). - * @property greenOffset - * @type Number - **/ - this.greenOffset = greenOffset || 0; - - /** - * Blue channel offset (added to value). - * @property blueOffset - * @type Number - **/ - this.blueOffset = blueOffset || 0; - - /** - * Alpha channel offset (added to value). - * @property alphaOffset - * @type Number - **/ - this.alphaOffset = alphaOffset || 0; - - this.FRAG_SHADER_BODY = ( - "uniform vec4 uColorMultiplier;" + - "uniform vec4 uColorOffset;" + - - "void main(void) {" + - "vec4 color = texture2D(uSampler, vRenderCoord);" + - - "gl_FragColor = (color * uColorMultiplier) + uColorOffset;" + - "}" - ); - - } - var p = createjs.extend(ColorFilter, createjs.Filter); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - -// public methods: - /** docced in super class **/ - p.shaderParamSetup = function(gl, stage, shaderProgram) { - gl.uniform4f( - gl.getUniformLocation(shaderProgram, "uColorMultiplier"), - this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier - ); - - gl.uniform4f( - gl.getUniformLocation(shaderProgram, "uColorOffset"), - this.redOffset/255, this.greenOffset/255, this.blueOffset/255, this.alphaOffset/255 - ); - }; - - /** docced in super class **/ - p.toString = function() { - return "[ColorFilter]"; - }; - - /** docced in super class **/ - p.clone = function() { - return new ColorFilter( - this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, - this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset - ); - }; - -// private methods: - /** docced in super class **/ - p._applyFilter = function(imageData) { - var data = imageData.data; - var l = data.length; - for (var i=0; iExample - * - * myColorMatrix.adjustHue(20).adjustBrightness(50); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}} - * for an example of how to use ColorMatrix to change a DisplayObject's color. - * @class ColorMatrix - * @param {Number} brightness - * @param {Number} contrast - * @param {Number} saturation - * @param {Number} hue - * @constructor - **/ - function ColorMatrix(brightness, contrast, saturation, hue) { - this.setColor(brightness, contrast, saturation, hue); - } - var p = ColorMatrix.prototype; - -// constants: - /** - * Array of delta values for contrast calculations. - * @property DELTA_INDEX - * @type Array - * @protected - * @static - **/ - ColorMatrix.DELTA_INDEX = [ - 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, - 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, - 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, - 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, - 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, - 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, - 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, - 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, - 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, - 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, - 10.0 - ]; - - /** - * Identity matrix values. - * @property IDENTITY_MATRIX - * @type Array - * @protected - * @static - **/ - ColorMatrix.IDENTITY_MATRIX = [ - 1,0,0,0,0, - 0,1,0,0,0, - 0,0,1,0,0, - 0,0,0,1,0, - 0,0,0,0,1 - ]; - - /** - * The constant length of a color matrix. - * @property LENGTH - * @type Number - * @protected - * @static - **/ - ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length; - - -// public methods: - /** - * Resets the instance with the specified values. - * @method setColor - * @param {Number} brightness - * @param {Number} contrast - * @param {Number} saturation - * @param {Number} hue - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - */ - p.setColor = function(brightness,contrast,saturation,hue) { - return this.reset().adjustColor(brightness,contrast,saturation,hue); - }; - - /** - * Resets the matrix to identity values. - * @method reset - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - */ - p.reset = function() { - return this.copy(ColorMatrix.IDENTITY_MATRIX); - }; - - /** - * Shortcut method to adjust brightness, contrast, saturation and hue. - * Equivalent to calling adjustHue(hue), adjustContrast(contrast), - * adjustBrightness(brightness), adjustSaturation(saturation), in that order. - * @method adjustColor - * @param {Number} brightness - * @param {Number} contrast - * @param {Number} saturation - * @param {Number} hue - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.adjustColor = function(brightness,contrast,saturation,hue) { - this.adjustHue(hue); - this.adjustContrast(contrast); - this.adjustBrightness(brightness); - return this.adjustSaturation(saturation); - }; - - /** - * Adjusts the brightness 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} value A value between -255 & 255 that will be added to the RGB channels. - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.adjustBrightness = function(value) { - if (value == 0 || isNaN(value)) { return this; } - value = this._cleanValue(value,255); - this._multiplyMatrix([ - 1,0,0,0,value, - 0,1,0,0,value, - 0,0,1,0,value, - 0,0,0,1,0, - 0,0,0,0,1 - ]); - return this; - }; - - /** - * Adjusts the contrast of pixel color. - * Positive values will increase contrast, negative values will decrease contrast. - * @method adjustContrast - * @param {Number} value A value between -100 & 100. - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.adjustContrast = function(value) { - if (value == 0 || isNaN(value)) { return this; } - value = this._cleanValue(value,100); - var x; - if (value<0) { - x = 127+value/100*127; - } else { - x = value%1; - if (x == 0) { - x = ColorMatrix.DELTA_INDEX[value]; - } else { - x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity. - } - x = x*127+127; - } - this._multiplyMatrix([ - x/127,0,0,0,0.5*(127-x), - 0,x/127,0,0,0.5*(127-x), - 0,0,x/127,0,0.5*(127-x), - 0,0,0,1,0, - 0,0,0,0,1 - ]); - return this; - }; - - /** - * Adjusts the color saturation of the pixel. - * Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale). - * @method adjustSaturation - * @param {Number} value A value between -100 & 100. - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.adjustSaturation = function(value) { - if (value == 0 || isNaN(value)) { return this; } - value = this._cleanValue(value,100); - var x = 1+((value > 0) ? 3*value/100 : value/100); - var lumR = 0.3086; - var lumG = 0.6094; - var lumB = 0.0820; - this._multiplyMatrix([ - lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0, - lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0, - lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0, - 0,0,0,1,0, - 0,0,0,0,1 - ]); - return this; - }; - - - /** - * Adjusts the hue of the pixel color. - * @method adjustHue - * @param {Number} value A value between -180 & 180. - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.adjustHue = function(value) { - if (value == 0 || isNaN(value)) { return this; } - value = this._cleanValue(value,180)/180*Math.PI; - var cosVal = Math.cos(value); - var sinVal = Math.sin(value); - var lumR = 0.213; - var lumG = 0.715; - var lumB = 0.072; - this._multiplyMatrix([ - lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0, - lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0, - lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0, - 0,0,0,1,0, - 0,0,0,0,1 - ]); - return this; - }; - - /** - * Concatenates (multiplies) the specified matrix with this one. - * @method concat - * @param {Array} matrix An array or ColorMatrix instance. - * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.) - * @chainable - **/ - p.concat = function(matrix) { - matrix = this._fixMatrix(matrix); - if (matrix.length != ColorMatrix.LENGTH) { return this; } - this._multiplyMatrix(matrix); - return this; - }; - - /** - * Returns a clone of this ColorMatrix. - * @method clone - * @return {ColorMatrix} A clone of this ColorMatrix. - **/ - p.clone = function() { - return (new ColorMatrix()).copy(this); - }; - - /** - * Return a length 25 (5x5) array instance containing this matrix's values. - * @method toArray - * @return {Array} An array holding this matrix's values. - **/ - p.toArray = function() { - var arr = []; - for (var i= 0, l=ColorMatrix.LENGTH; i ColorMatrix.LENGTH) { - matrix = matrix.slice(0,ColorMatrix.LENGTH); - } - return matrix; - }; - - - createjs.ColorMatrix = ColorMatrix; -}()); - -//############################################################################## -// ColorMatrixFilter.js -//############################################################################## - -this.createjs = this.createjs||{}; - -(function() { - "use strict"; - - -// constructor: - /** - * Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the - * {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform, - * consider the {{#crossLink "ColorFilter"}}{{/crossLink}}. - * - *

      Example

      - * This example creates a red circle, inverts its hue, and then saturates it to brighten it up. - * - * var shape = new createjs.Shape().set({x:100,y:100}); - * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50); - * - * var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100); - * shape.filters = [ - * new createjs.ColorMatrixFilter(matrix) - * ]; - * - * shape.cache(-50, -50, 100, 100); - * - * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters. - * @class ColorMatrixFilter - * @constructor - * @extends Filter - * @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}} - * class. - **/ - function ColorMatrixFilter(matrix) { - this.Filter_constructor(); - - // public properties: - /** - * A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}} - * @property matrix - * @type Array | ColorMatrix - **/ - this.matrix = matrix; - - this.FRAG_SHADER_BODY = ( - "uniform mat4 uColorMatrix;" + - "uniform vec4 uColorMatrixOffset;" + - - "void main(void) {" + - "vec4 color = texture2D(uSampler, vRenderCoord);" + - - "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];" + - - "gl_FragColor = newColor + uColorMatrixOffset;" + - "}" - ); - } - var p = createjs.extend(ColorMatrixFilter, createjs.Filter); - - // TODO: deprecated - // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details. - - /** docced in super class **/ - p.shaderParamSetup = function(gl, stage, shaderProgram) { - var mat = this.matrix; - var colorMatrix = new Float32Array([ - mat[0],mat[1],mat[2],mat[3], - mat[5],mat[6],mat[7],mat[8], - mat[10],mat[11],mat[12],mat[13], - mat[15],mat[16],mat[17],mat[18] - ]); - - gl.uniformMatrix4fv( - gl.getUniformLocation(shaderProgram, "uColorMatrix"), - false, colorMatrix - ); - gl.uniform4f( - gl.getUniformLocation(shaderProgram, "uColorMatrixOffset"), - mat[4]/255, mat[9]/255, mat[14]/255, mat[19]/255 - ); - }; - -// public methods: - /** docced in super class **/ - p.toString = function() { - return "[ColorMatrixFilter]"; - }; - - /** docced in super class **/ - p.clone = function() { - return new ColorMatrixFilter(this.matrix); - }; - -// private methods: - /** docced in super class **/ - p._applyFilter = function(imageData) { - var data = imageData.data; - var l = data.length; - var r,g,b,a; - var mtx = this.matrix; - var m0 = mtx[0], m1 = mtx[1], m2 = mtx[2], m3 = mtx[3], m4 = mtx[4]; - var m5 = mtx[5], m6 = mtx[6], m7 = mtx[7], m8 = mtx[8], m9 = mtx[9]; - var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14]; - var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19]; - - for (var i=0; iExample - * - * var stage = new createjs.Stage("canvasId"); - * createjs.Touch.enable(stage); - * - * Note: It is important to disable Touch on a stage that you are no longer using: - * - * createjs.Touch.disable(stage); - * - * @class Touch - * @static - **/ - function Touch() { - throw "Touch cannot be instantiated"; - } - - -// public static methods: - /** - * Returns `true` if touch is supported in the current browser. - * @method isSupported - * @return {Boolean} Indicates whether touch is supported in the current browser. - * @static - **/ - Touch.isSupported = function() { - return !!(('ontouchstart' in window) // iOS & Android - || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10 - || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+ - }; - - /** - * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS - * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and - * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for - * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}} - * for more information. - * @method enable - * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on. - * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time. - * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be - * allowed when the user is interacting with the target canvas. - * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage. - * @static - **/ - Touch.enable = function(stage, singleTouch, allowDefault) { - if (!stage || !stage.canvas || !Touch.isSupported()) { return false; } - if (stage.__touch) { return true; } - - // inject required properties on stage: - stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0}; - - // note that in the future we may need to disable the standard mouse event model before adding - // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though. - if ('ontouchstart' in window) { Touch._IOS_enable(stage); } - else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); } - return true; - }; - - /** - * Removes all listeners that were set up when calling `Touch.enable()` on a stage. - * @method disable - * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on. - * @static - **/ - Touch.disable = function(stage) { - if (!stage) { return; } - if ('ontouchstart' in window) { Touch._IOS_disable(stage); } - else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); } - - delete stage.__touch; - }; - - -// Private static methods: - /** - * @method _IOS_enable - * @protected - * @param {Stage} stage - * @static - **/ - Touch._IOS_enable = function(stage) { - var canvas = stage.canvas; - var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); }; - canvas.addEventListener("touchstart", f, false); - canvas.addEventListener("touchmove", f, false); - canvas.addEventListener("touchend", f, false); - canvas.addEventListener("touchcancel", f, false); - }; - - /** - * @method _IOS_disable - * @protected - * @param {Stage} stage - * @static - **/ - Touch._IOS_disable = function(stage) { - var canvas = stage.canvas; - if (!canvas) { return; } - var f = stage.__touch.f; - canvas.removeEventListener("touchstart", f, false); - canvas.removeEventListener("touchmove", f, false); - canvas.removeEventListener("touchend", f, false); - canvas.removeEventListener("touchcancel", f, false); - }; - - /** - * @method _IOS_handleEvent - * @param {Stage} stage - * @param {Object} e The event to handle - * @protected - * @static - **/ - Touch._IOS_handleEvent = function(stage, e) { - if (!stage) { return; } - if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); } - var touches = e.changedTouches; - var type = e.type; - for (var i= 0,l=touches.length; i Date: Fri, 27 May 2022 14:17:32 +0530 Subject: [PATCH 2/2] Chrome browser - Touch drag fix --- lib/easeljs.js | 652 ++++++++++++++++++++++++++----------------------- 1 file changed, 341 insertions(+), 311 deletions(-) diff --git a/lib/easeljs.js b/lib/easeljs.js index 917b11b41..4abd6738e 100644 --- a/lib/easeljs.js +++ b/lib/easeljs.js @@ -7871,8 +7871,6 @@ this.createjs = this.createjs||{}; // Stage.js //############################################################################## -this.createjs = this.createjs||{}; - (function() { "use strict"; @@ -7903,8 +7901,8 @@ this.createjs = this.createjs||{}; **/ function Stage(canvas) { this.Container_constructor(); - - + + // public properties: /** * Indicates whether the stage should automatically clear the canvas before each render. You can set this to false @@ -7921,7 +7919,7 @@ this.createjs = this.createjs||{}; * @default true **/ this.autoClear = true; - + /** * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the * first stage that will be ticked (or they will clear each other's render). @@ -7937,7 +7935,7 @@ this.createjs = this.createjs||{}; * @type HTMLCanvasElement | Object **/ this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas; - + /** * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent * position over the canvas, and mouseInBounds will be set to false. @@ -7946,7 +7944,7 @@ this.createjs = this.createjs||{}; * @readonly **/ this.mouseX = 0; - + /** * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent * position over the canvas, and mouseInBounds will be set to false. @@ -7955,7 +7953,7 @@ this.createjs = this.createjs||{}; * @readonly **/ this.mouseY = 0; - + /** * Specifies the area of the stage to affect when calling update. This can be use to selectively * re-draw specific regions of the canvas. If null, the whole canvas area is drawn. @@ -7963,7 +7961,7 @@ this.createjs = this.createjs||{}; * @type {Rectangle} */ this.drawRect = null; - + /** * Indicates whether display objects should be rendered on whole pixels. You can set the * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of @@ -7973,7 +7971,7 @@ this.createjs = this.createjs||{}; * @default false **/ this.snapToPixelEnabled = false; - + /** * Indicates whether the mouse is currently within the bounds of the canvas. * @property mouseInBounds @@ -7981,7 +7979,7 @@ this.createjs = this.createjs||{}; * @default false **/ this.mouseInBounds = false; - + /** * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas. * @property tickOnUpdate @@ -7989,7 +7987,7 @@ this.createjs = this.createjs||{}; * @default true **/ this.tickOnUpdate = true; - + /** * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}} @@ -7999,8 +7997,8 @@ this.createjs = this.createjs||{}; * @default false **/ this.mouseMoveOutside = false; - - + + /** * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas. * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas. @@ -8009,15 +8007,15 @@ this.createjs = this.createjs||{}; * @default true **/ this.preventSelection = true; - + /** * The hitArea property is not supported for Stage. * @property hitArea * @type {DisplayObject} * @default null */ - - + + // private properties: /** * Holds objects with data for each active pointer id. Each object has the following properties: @@ -8027,7 +8025,7 @@ this.createjs = this.createjs||{}; * @private */ this._pointerData = {}; - + /** * Number of active pointers. * @property _pointerCount @@ -8035,7 +8033,7 @@ this.createjs = this.createjs||{}; * @private */ this._pointerCount = 0; - + /** * The ID of the primary pointer. * @property _primaryPointerID @@ -8043,29 +8041,29 @@ this.createjs = this.createjs||{}; * @private */ this._primaryPointerID = null; - + /** * @property _mouseOverIntervalID * @protected * @type Number **/ this._mouseOverIntervalID = null; - + /** * @property _nextStage * @protected * @type Stage **/ this._nextStage = null; - + /** * @property _prevStage * @protected * @type Stage **/ this._prevStage = null; - - + + // initialize: this.enableDOMEvents(true); } @@ -8109,35 +8107,35 @@ this.createjs = this.createjs||{}; * @event mouseenter * @since 0.7.0 */ - + /** * Dispatched each update immediately before the tick event is propagated through the display list. * You can call preventDefault on the event object to cancel propagating the tick event. * @event tickstart * @since 0.7.0 */ - + /** * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if * tickOnUpdate is false. Precedes the "drawstart" event. * @event tickend * @since 0.7.0 */ - + /** * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. * You can call preventDefault on the event object to cancel the draw. * @event drawstart * @since 0.7.0 */ - + /** * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored. * @event drawend * @since 0.7.0 */ - + // getter / setters: /** * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them. @@ -8147,7 +8145,7 @@ this.createjs = this.createjs||{}; * topStage.nextStage = bottomStage; * * To disable relaying, set nextStage to null. - * + * * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled. * Considerations when using roll over in relay targets:
        @@ -8160,12 +8158,12 @@ this.createjs = this.createjs||{}; * topStage.nextStage = targetStage; * topStage.enableMouseOver(10); * targetStage.enableMouseOver(30); - * + * * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its * DOM events using: - * + * * targetStage.enableDOMEvents(false); - * + * * @property nextStage * @type {Stage} **/ @@ -8177,7 +8175,7 @@ this.createjs = this.createjs||{}; if (value) { value._prevStage = this; } this._nextStage = value; }; - + try { Object.defineProperties(p, { nextStage: { get: p._get_nextStage, set: p._set_nextStage } @@ -8216,7 +8214,7 @@ this.createjs = this.createjs||{}; ctx.restore(); this.dispatchEvent("drawend"); }; - + /** * 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. @@ -8232,18 +8230,18 @@ this.createjs = this.createjs||{}; * function handleTick(evtObj) { * // clone the event object from Ticker, and add some custom data to it: * var evt = evtObj.clone().set({greeting:"hello", name:"world"}); - * + * * // pass it to stage.update(): * myStage.update(evt); // subsequently calls tick() with the same param * } - * + * * // ... * myDisplayObject.on("tick", handleDisplayObjectTick); * function handleDisplayObjectTick(evt) { * console.log(evt.delta); // the delta property from the Ticker tick event object * console.log(evt.greeting, evt.name); // custom data: "hello world" * } - * + * * @method tick * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property. **/ @@ -8303,7 +8301,7 @@ this.createjs = this.createjs||{}; data = ctx.getImageData(0, 0, w, h); var compositeOperation = ctx.globalCompositeOperation; ctx.globalCompositeOperation = "destination-over"; - + ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, w, h); } @@ -8364,40 +8362,33 @@ this.createjs = this.createjs||{}; * @method enableDOMEvents * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true. **/ - p.enableDOMEvents = function(enable) { if (enable == null) { enable = true; } var n, o, ls = this._eventListeners; if (!enable && ls) { for (n in ls) { o = ls[n]; - if(n!=="mousemove"){ - o.t.removeEventListener(n, o.f, false); - delete this._eventListeners[n]; - } + o.t.removeEventListener(n, o.f, false); } - } else if (enable && this.canvas) { + this._eventListeners = null; + } else if (enable && !ls && this.canvas) { var t = window.addEventListener ? window : document; var _this = this; - if(this._eventListeners===undefined)ls=this._eventListeners={}; - if(ls["mousemove"]===undefined){ - ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} }; - ls["mousemove"].t.addEventListener("mousemove",ls["mousemove"].f, false); - } - if(ls["mouseup"]===undefined){ - ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} }; - ls["mouseup"].t.addEventListener("mouseup",ls["mouseup"].f, false); - } - if(ls["dblclick"]===undefined){ - ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} }; - ls["dblclick"].t.addEventListener("dblclick",ls["dblclick"].f, false); - } - if(ls["mousedown"]===undefined){ - ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} }; - ls["mousedown"].t.addEventListener("mousedown",ls["mousedown"].f, false); + ls = this._eventListeners = {}; + + // Dan Zen adjust October 28 2019 - added pointerCheck so these do not fire if pointer events have fired + ls["mousedown"] = {t:this.canvas, f:function(e) {if (!pointerCheck) _this._handleMouseDown(e)} }; + ls["mouseup"] = {t:t, f:function(e) {if (!pointerCheck) _this._handleMouseUp(e)} }; + + ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} }; + ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} }; + + for (n in ls) { + o = ls[n]; + o.t.addEventListener(n, o.f, false); } } - }; + }; /** * Stage instances cannot be cloned. @@ -8487,11 +8478,11 @@ this.createjs = this.createjs||{}; if (id === -1 && o.inBounds == !inBounds) { this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e); } - + this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e); this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e); } - + nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null); }; @@ -8552,20 +8543,20 @@ this.createjs = this.createjs||{}; p._handlePointerUp = function(id, e, clear, owner) { var nextStage = this._nextStage, o = this._getPointerData(id); if (this._prevStage && owner === undefined) { return; } // redundant listener. - + var target=null, oTarget = o.target; if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); } - + if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; } - + if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); } this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e); - + if (clear) { if (id==this._primaryPointerID) { this._primaryPointerID = null; } delete(this._pointerData[id]); } else { o.target = null; } - + nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this); }; @@ -8590,14 +8581,14 @@ this.createjs = this.createjs||{}; p._handlePointerDown = function(id, e, pageX, pageY, owner) { if (this.preventSelection) { e.preventDefault(); } if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over. - + if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); } var target = null, nextStage = this._nextStage, o = this._getPointerData(id); if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); } if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; } this._dispatchMouseEvent(target, "mousedown", true, id, o, e); - + nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this); }; @@ -8610,7 +8601,7 @@ this.createjs = this.createjs||{}; **/ p._testMouseOver = function(clear, owner, eventTarget) { if (this._prevStage && owner === undefined) { return; } // redundant listener. - + var nextStage = this._nextStage; if (!this._mouseOverIntervalID) { // not enabled for mouseover, but should still relay the event. @@ -8620,11 +8611,11 @@ this.createjs = this.createjs||{}; var o = this._getPointerData(-1); // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs. if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; } - + var e = o.posEvtObj; var isEventTarget = eventTarget || e&&(e.target == this.canvas); var target=null, common = -1, cursor="", t, i, l; - + if (!owner && (clear || this.mouseInBounds && isEventTarget)) { target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true); this._mouseOverX = this.mouseX; @@ -8666,7 +8657,7 @@ this.createjs = this.createjs||{}; if (oldTarget != target) { this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget); } - + nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this); }; @@ -8713,6 +8704,7 @@ this.createjs = this.createjs||{}; createjs.Stage = createjs.promote(Stage, "Container"); }()); + //############################################################################## // StageGL.js //############################################################################## @@ -16341,9 +16333,12 @@ this.createjs = this.createjs||{}; //############################################################################## // Touch.js +// updated to https://github.com/CreateJS/EaselJS/commit/848dcbf765abd00234569fd348e4402e329f5708 +// Sep 6, 2018 //############################################################################## -this.createjs = this.createjs||{}; +// Dan Zen change October 28 2019 - used in a couple places below +var pointerCheck = false; (function() { "use strict"; @@ -16373,263 +16368,298 @@ this.createjs = this.createjs||{}; throw "Touch cannot be instantiated"; } + // public static methods: + /** + * Returns `true` if touch is supported in the current browser. + * @method isSupported + * @return {Boolean} Indicates whether touch is supported in the current browser. + * @static + **/ + Touch.isSupported = function() { + return !!( + ('ontouchstart' in window) // iOS & Android + + // https://github.com/owendwyer addition June 10 2019 + // to include new Firefox which now turns off some touch events in the desktop browser to avoid being seen as mobile + // https://developer.mozilla.org/en-US/docs/Web/API/Touch_events#Browser_compatibility + || window.TouchEvent + + || (window.MSPointerEvent && window.navigator.msMaxTouchPoints > 0) // IE10 + || (window.PointerEvent && window.navigator.maxTouchPoints > 0) + ); // IE11+ + }; -// public static methods: - /** - * Returns `true` if touch is supported in the current browser. - * @method isSupported - * @return {Boolean} Indicates whether touch is supported in the current browser. - * @static - **/ - Touch.isSupported = function() { - return !!(('ontouchstart' in window) // iOS & Android - || window.TouchEvent - || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10 - || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+ - }; - - /** - * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS - * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and - * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for - * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}} - * for more information. - * @method enable - * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on. - * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time. - * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be - * allowed when the user is interacting with the target canvas. - * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage. - * @static - **/ - Touch.enable = function(stage, singleTouch, allowDefault) { - if (!stage || !stage.canvas || !Touch.isSupported()) { return false; } - if (stage.__touch) { return true; } - - // inject required properties on stage: - stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0}; - - // note that in the future we may need to disable the standard mouse event model before adding - // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though. - if ('ontouchstart' in window) { Touch._IOS_enable(stage); } - else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); } - return true; - }; - - /** - * Removes all listeners that were set up when calling `Touch.enable()` on a stage. - * @method disable - * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on. - * @static - **/ - Touch.disable = function(stage) { - if (!stage||!stage.__touch) { return; } - if ('ontouchstart' in window) { Touch._IOS_disable(stage); } - else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); } - - delete stage.__touch; - }; - - -// Private static methods: - /** - * @method _IOS_enable - * @protected - * @param {Stage} stage - * @static - **/ - Touch._IOS_enable = function(stage) { - var canvas = stage.canvas; - var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); }; - canvas.addEventListener("touchstart", f, false); - canvas.addEventListener("touchmove", f, false); - canvas.addEventListener("touchend", f, false); - canvas.addEventListener("touchcancel", f, false); - }; + /** + * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS + * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and + * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for + * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}} + * for more information. + * @method enable + * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on. + * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time. + * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be + * allowed when the user is interacting with the target canvas. + * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage. + * @static + **/ + Touch.enable = function(stage, singleTouch, allowDefault) { + if (!stage || !stage.canvas || !Touch.isSupported()) { return false; } + if (stage.__touch) { return true; } - /** - * @method _IOS_disable - * @protected - * @param {Stage} stage - * @static - **/ - Touch._IOS_disable = function(stage) { - var canvas = stage.canvas; - if (!canvas) { return; } - var f = stage.__touch.f; - canvas.removeEventListener("touchstart", f, false); - canvas.removeEventListener("touchmove", f, false); - canvas.removeEventListener("touchend", f, false); - canvas.removeEventListener("touchcancel", f, false); - }; + // inject required properties on stage: + stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0}; - /** - * @method _IOS_handleEvent - * @param {Stage} stage - * @param {Object} e The event to handle - * @protected - * @static - **/ - Touch._IOS_handleEvent = function(stage, e) { - if (!stage) { return; } - if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); } - var touches = e.changedTouches; - var type = e.type; - for (var i= 0,l=touches.length; i