1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * <p>
 29  *    A cc.SpriteFrame has:<br/>
 30  *      - texture: A cc.Texture2D that will be used by the cc.Sprite<br/>
 31  *      - rectangle: A rectangle of the texture<br/>
 32  *    <br/>
 33  *    You can modify the frame of a cc.Sprite by doing:<br/>
 34  * </p>
 35  * @class
 36  * @extends cc.Class
 37  *
 38  * @example
 39  * var texture = cc.textureCache.addImage(s_dragon_animation);
 40  * var frame0 = cc.SpriteFrame.create(texture, cc.rect(132 * 0, 132 * 0, 132, 132));
 41  */
 42 cc.SpriteFrame = cc.Class.extend(/** @lends cc.SpriteFrame# */{
 43     _offset:null,
 44     _originalSize:null,
 45     _rectInPixels:null,
 46     _rotated:false,
 47     _rect:null,
 48     _offsetInPixels:null,
 49     _originalSizeInPixels:null,
 50     _texture:null,
 51     _textureFilename:"",
 52     _textureLoaded:false,
 53     _eventListeners:null,
 54 
 55     /**
 56      * <p>
 57      *    Create a cc.SpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels.<br/>
 58      *    The originalSize is the size in pixels of the frame before being trimmed.
 59      * </p>
 60      * @constructor
 61      * @param {String|cc.Texture2D} filename
 62      * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
 63      * @param {Boolean} rotated
 64      * @param {cc.Point} offset
 65      * @param {cc.Size} originalSize
 66      * @example
 67      * // 1.Create a cc.SpriteFrame with image path
 68      * var frame1 = new cc.SpriteFrame("res/grossini_dance.png",cc.rect(0,0,90,128));
 69      * var frame2 = new cc.SpriteFrame("res/grossini_dance.png",cc.rect(0,0,90,128),false,0,cc.size(90,128));
 70      *
 71      * // 2.Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
 72      * var texture = cc.textureCache.addImage("res/grossini_dance.png");
 73      * var frame1 = new cc.SpriteFrame(texture, cc.rect(0,0,90,128));
 74      * var frame2 = new cc.SpriteFrame(texture, cc.rect(0,0,90,128),false,0,cc.size(90,128));
 75      */
 76     ctor:function (filename, rect, rotated, offset, originalSize) {
 77         this._offset = cc.p(0, 0);
 78         this._offsetInPixels = cc.p(0, 0);
 79         this._originalSize = cc.size(0, 0);
 80         this._rotated = false;
 81         this._originalSizeInPixels = cc.size(0, 0);
 82         this._textureFilename = "";
 83         this._texture = null;
 84         this._textureLoaded = false;
 85 
 86         if(filename !== undefined && rect !== undefined ){
 87             if(rotated === undefined || offset === undefined || originalSize === undefined){
 88                 this.initWithTexture(filename, rect);
 89             }
 90             else{
 91                 this.initWithTexture(filename, rect, rotated, offset, originalSize)
 92             }
 93         }
 94     },
 95 
 96     // attributes
 97     textureLoaded:function(){
 98         return this._textureLoaded;
 99     },
100 
101     addLoadedEventListener:function(callback, target){
102         if (this._eventListeners == null){
103            this._eventListeners = [];
104         }
105         this._eventListeners.push({eventCallback:callback, eventTarget:target});
106     },
107 
108     _callLoadedEventCallbacks:function(){
109         var locListeners = this._eventListeners;
110         if (!locListeners) return;
111         for(var i = 0, len = locListeners.length;  i < len; i++){
112             var selCallback = locListeners[i];
113             selCallback.eventCallback.call(selCallback.eventTarget, this);
114         }
115         locListeners.length = 0;
116     },
117 
118     /**
119      * @return {cc.Rect}
120      */
121     getRectInPixels:function () {
122         var locRectInPixels = this._rectInPixels;
123         return cc.rect(locRectInPixels.x, locRectInPixels.y, locRectInPixels.width, locRectInPixels.height);
124     },
125 
126     /**
127      * @param {cc.Rect} rectInPixels
128      */
129     setRectInPixels:function (rectInPixels) {
130         if (!this._rectInPixels){
131             this._rectInPixels = cc.rect(0,0,0,0);
132         }
133         this._rectInPixels.x = rectInPixels.x;
134         this._rectInPixels.y = rectInPixels.y;
135         this._rectInPixels.width = rectInPixels.width;
136         this._rectInPixels.height = rectInPixels.height;
137         this._rect = cc.rectPixelsToPoints(rectInPixels);
138     },
139 
140     /**
141      * <p>
142      *     return is rotated of SpriteFrame. <br/>
143      * </p>
144      * @return {Boolean}
145      */
146     isRotated:function () {
147         return this._rotated;
148     },
149 
150     /**
151      * set SpriteFrame is rotated
152      * @param {Boolean} bRotated
153      */
154     setRotated:function (bRotated) {
155         this._rotated = bRotated;
156     },
157 
158     /**
159      * get rect of the frame
160      * @return {cc.Rect}
161      */
162     getRect:function () {
163         var locRect = this._rect;
164         return cc.rect(locRect.x, locRect.y, locRect.width, locRect.height);
165     },
166 
167     /**
168      * set rect of the frame
169      * @param {cc.Rect} rect
170      */
171     setRect:function (rect) {
172         if (!this._rect){
173             this._rect = cc.rect(0,0,0,0);
174         }
175         this._rect.x = rect.x;
176         this._rect.y = rect.y;
177         this._rect.width = rect.width;
178         this._rect.height = rect.height;
179         this._rectInPixels = cc.rectPointsToPixels(this._rect);
180     },
181 
182     /**
183      * get offset of the frame
184      * @return {cc.Point}
185      */
186     getOffsetInPixels:function () {
187         return this._offsetInPixels;
188     },
189 
190     /**
191      * set offset of the frame
192      * @param {cc.Point} offsetInPixels
193      */
194     setOffsetInPixels:function (offsetInPixels) {
195         this._offsetInPixels.x = offsetInPixels.x;
196         this._offsetInPixels.y = offsetInPixels.y;
197         cc._pointPixelsToPointsOut(this._offsetInPixels, this._offset);
198     },
199 
200     /**
201      * get original size of the trimmed image
202      * @const
203      * @return {cc.Size}
204      */
205     getOriginalSizeInPixels:function () {
206         return this._originalSizeInPixels;
207     },
208 
209     /**
210      * set original size of the trimmed image
211      * @param {cc.Size} sizeInPixels
212      */
213     setOriginalSizeInPixels:function (sizeInPixels) {
214         this._originalSizeInPixels.width = sizeInPixels.width;
215         this._originalSizeInPixels.height = sizeInPixels.height;
216     },
217 
218     /**
219      * get original size of the trimmed image
220      * @const
221      * @return {cc.Size}
222      */
223     getOriginalSize:function () {
224         return this._originalSize;
225     },
226 
227     /**
228      * set original size of the trimmed image
229      * @param {cc.Size} sizeInPixels
230      */
231     setOriginalSize:function (sizeInPixels) {
232         this._originalSize.width = sizeInPixels.width;
233         this._originalSize.height = sizeInPixels.height;
234     },
235 
236     /**
237      * get texture of the frame
238      * @return {cc.Texture2D}
239      */
240     getTexture:function () {
241         if (this._texture)
242             return this._texture;
243         if (this._textureFilename !== "") {
244             var locTexture = cc.textureCache.addImage(this._textureFilename);
245             if (locTexture)
246                 this._textureLoaded = locTexture.isLoaded();
247             return locTexture;
248         }
249         return null;
250     },
251 
252     /**
253      * set texture of the frame, the texture is retained
254      * @param {cc.Texture2D} texture
255      */
256     setTexture:function (texture) {
257         if (this._texture != texture) {
258             var locLoaded = texture.isLoaded();
259             this._textureLoaded = locLoaded;
260             this._texture = texture;
261             if(!locLoaded){
262                 texture.addLoadedEventListener(function(sender){
263                     this._textureLoaded = true;
264                     if(this._rotated && cc._renderType === cc._RENDER_TYPE_CANVAS){
265                         var tempElement = sender.getHtmlElementObj();
266                         tempElement = cc.cutRotateImageToCanvas(tempElement, this.getRect());
267                         var tempTexture = new cc.Texture2D();
268                         tempTexture.initWithElement(tempElement);
269                         tempTexture.handleLoadedTexture();
270                         this.setTexture(tempTexture);
271 
272                         var rect = this.getRect();
273                         this.setRect(cc.rect(0, 0, rect.width, rect.height));
274                     }
275                     var locRect = this._rect;
276                     if(locRect.width === 0 && locRect.height === 0){
277                         var w = sender.width, h = sender.height;
278                         this._rect.width = w;
279                         this._rect.height = h;
280                         this._rectInPixels = cc.rectPointsToPixels(this._rect);
281                         this._originalSizeInPixels.width = this._rectInPixels.width;
282                         this._originalSizeInPixels.height = this._rectInPixels.height;
283                         this._originalSize.width =  w;
284                         this._originalSize.height =  h;
285                     }
286                     this._callLoadedEventCallbacks();
287                 }, this);
288             }
289         }
290     },
291 
292     /**
293      * Offset getter
294      * @const
295      * @return {cc.Point}
296      */
297     getOffset:function () {
298         return this._offset;
299     },
300 
301     /**
302      * offset setter
303      * @param {cc.Point} offsets
304      */
305     setOffset:function (offsets) {
306         this._offset.x = offsets.x;
307         this._offset.y = offsets.y;
308     },
309 
310     clone: function(){
311         var frame = new cc.SpriteFrame();
312         frame.initWithTexture(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels);
313         frame.setTexture(this._texture);
314         return frame;
315     },
316 
317     /**
318      * copy a new SpriteFrame
319      * @return {cc.SpriteFrame}
320      */
321     copyWithZone:function () {
322         var copy = new cc.SpriteFrame();
323         copy.initWithTexture(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels);
324         copy.setTexture(this._texture);
325         return copy;
326     },
327 
328     copy:function () {
329         return this.copyWithZone();
330     },
331 
332     /**
333      * Initializes SpriteFrame with Texture, rect, rotated, offset and originalSize in pixels.
334      * @param {String|cc.Texture2D} texture
335      * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
336      * @param {Boolean} [rotated=false]
337      * @param {cc.Point} [offset=cc.p(0,0)]
338      * @param {cc.Size} [originalSize=rect.size]
339      * @return {Boolean}
340      */
341     initWithTexture:function (texture, rect, rotated, offset, originalSize) {
342         if(arguments.length === 2)
343             rect = cc.rectPointsToPixels(rect);
344 
345         offset = offset || cc.p(0, 0);
346         originalSize = originalSize || rect;
347         rotated = rotated || false;
348 
349         if (typeof(texture) == "string"){
350             this._texture = null;
351             this._textureFilename = texture;
352         } else if (texture instanceof cc.Texture2D){
353             this.setTexture(texture);
354         }
355 
356         this._rectInPixels = rect;
357         this._rect = cc.rectPixelsToPoints(rect);
358         this._offsetInPixels.x = offset.x;
359         this._offsetInPixels.y = offset.y;
360         cc._pointPixelsToPointsOut(offset, this._offset);
361         this._originalSizeInPixels.width = originalSize.width;
362         this._originalSizeInPixels.height = originalSize.height;
363         cc._sizePixelsToPointsOut(originalSize, this._originalSize);
364         this._rotated = rotated;
365         return true;
366     }
367 });
368 
369 /**
370  * <p>
371  *    Create a cc.SpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels.<br/>
372  *    The originalSize is the size in pixels of the frame before being trimmed.
373  * </p>
374  * @param {String|cc.Texture2D} filename
375  * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
376  * @param {Boolean} rotated
377  * @param {cc.Point} offset
378  * @param {cc.Size} originalSize
379  * @return {cc.SpriteFrame}
380  * @example
381  * 1.
382  * //Create a cc.SpriteFrame with image path
383  * var frame1 = cc.SpriteFrame.create("res/grossini_dance.png",cc.rect(0,0,90,128));
384  * var frame2 = cc.SpriteFrame.create("res/grossini_dance.png",cc.rect(0,0,90,128),false,0,cc.size(90,128));
385  *
386  * 2.
387  * //Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
388  * var texture = cc.textureCache.addImage("res/grossini_dance.png");
389  * var frame1 = cc.SpriteFrame.create(texture, cc.rect(0,0,90,128));
390  * var frame2 = cc.SpriteFrame.create(texture, cc.rect(0,0,90,128),false,0,cc.size(90,128));
391  */
392 cc.SpriteFrame.create = function (filename, rect, rotated, offset, originalSize) {
393     return new cc.SpriteFrame(filename,rect,rotated,offset,originalSize);
394 };
395 
396 cc.SpriteFrame._frameWithTextureForCanvas = function (texture, rect, rotated, offset, originalSize) {
397     var spriteFrame = new cc.SpriteFrame();
398     spriteFrame._texture = texture;
399     spriteFrame._rectInPixels = rect;
400     spriteFrame._rect = cc.rectPixelsToPoints(rect);
401     spriteFrame._offsetInPixels.x = offset.x;
402     spriteFrame._offsetInPixels.y = offset.y;
403     cc._pointPixelsToPointsOut(spriteFrame._offsetInPixels, spriteFrame._offset);
404     spriteFrame._originalSizeInPixels.width = originalSize.width;
405     spriteFrame._originalSizeInPixels.height = originalSize.height;
406     cc._sizePixelsToPointsOut(spriteFrame._originalSizeInPixels, spriteFrame._originalSize);
407     spriteFrame._rotated = rotated;
408     return spriteFrame;
409 };
410