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.getInstance().addImage(s_dragon_animation);
 40  * var frame0 = cc.SpriteFrame.createWithTexture(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     ctor:function () {
 56         this._offset = cc.p(0, 0);
 57         this._offsetInPixels = cc.p(0, 0);
 58         this._originalSize = cc.size(0, 0);
 59         this._rectInPixels = cc.rect(0, 0, 0, 0);
 60         this._rotated = false;
 61         this._rect = cc.rect(0, 0, 0, 0);
 62         this._originalSizeInPixels = cc.size(0, 0);
 63         this._textureFilename = "";
 64         this._texture = null;
 65         this._textureLoaded = false;
 66         this._eventListeners = [];
 67     },
 68 
 69     // attributes
 70     textureLoaded:function(){
 71         return this._textureLoaded;
 72     },
 73 
 74     addLoadedEventListener:function(callback, target){
 75         this._eventListeners.push({eventCallback:callback, eventTarget:target});
 76     },
 77 
 78     _callLoadedEventCallbacks:function(){
 79         var locListeners = this._eventListeners;
 80         for(var i = 0, len = locListeners.length;  i < len; i++){
 81             var selCallback = locListeners[i];
 82             selCallback.eventCallback.call(selCallback.eventTarget, this);
 83         }
 84         locListeners.length = 0;
 85     },
 86 
 87     /**
 88      * @return {cc.Rect}
 89      */
 90     getRectInPixels:function () {
 91         var locRectInPixels = this._rectInPixels;
 92         return cc.rect(locRectInPixels.x, locRectInPixels.y, locRectInPixels.width, locRectInPixels.height);
 93     },
 94 
 95     /**
 96      * @param {cc.Rect} rectInPixels
 97      */
 98     setRectInPixels:function (rectInPixels) {
 99         this._rectInPixels.x = rectInPixels.x;
100         this._rectInPixels.y = rectInPixels.y;
101         this._rectInPixels.width = rectInPixels.width;
102         this._rectInPixels.height = rectInPixels.height;
103         this._rect = cc.RECT_PIXELS_TO_POINTS(rectInPixels);
104     },
105 
106     /**
107      * <p>
108      *     return is rotated of SpriteFrame. <br/>
109      * </p>
110      * @return {Boolean}
111      */
112     isRotated:function () {
113         return this._rotated;
114     },
115 
116     /**
117      * set SpriteFrame is rotated
118      * @param {Boolean} bRotated
119      */
120     setRotated:function (bRotated) {
121         this._rotated = bRotated;
122     },
123 
124     /**
125      * get rect of the frame
126      * @return {cc.Rect}
127      */
128     getRect:function () {
129         var locRect = this._rect;
130         return cc.rect(locRect.x, locRect.y, locRect.width, locRect.height);
131     },
132 
133     /**
134      * set rect of the frame
135      * @param {cc.Rect} rect
136      */
137     setRect:function (rect) {
138         this._rect.x = rect.x;
139         this._rect.y = rect.y;
140         this._rect.width = rect.width;
141         this._rect.height = rect.height;
142         this._rectInPixels = cc.RECT_POINTS_TO_PIXELS(this._rect);
143     },
144 
145     /**
146      * get offset of the frame
147      * @return {cc.Point}
148      */
149     getOffsetInPixels:function () {
150         return cc.p(this._offsetInPixels.x, this._offsetInPixels.y);
151     },
152 
153     /**
154      * set offset of the frame
155      * @param {cc.Point} offsetInPixels
156      */
157     setOffsetInPixels:function (offsetInPixels) {
158         this._offsetInPixels.x = offsetInPixels.x;
159         this._offsetInPixels.y = offsetInPixels.y;
160         this._offset = cc.POINT_PIXELS_TO_POINTS(this._offsetInPixels);
161     },
162 
163     /**
164      * get original size of the trimmed image
165      * @const
166      * @return {cc.Size}
167      */
168     getOriginalSizeInPixels:function () {
169         return cc.size(this._originalSizeInPixels.width, this._originalSizeInPixels.height);
170     },
171 
172     /**
173      * set original size of the trimmed image
174      * @param {cc.Size} sizeInPixels
175      */
176     setOriginalSizeInPixels:function (sizeInPixels) {
177         this._originalSizeInPixels.width = sizeInPixels.width;
178         this._originalSizeInPixels.height = sizeInPixels.height;
179     },
180 
181     /**
182      * get original size of the trimmed image
183      * @const
184      * @return {cc.Size}
185      */
186     getOriginalSize:function () {
187         return cc.size(this._originalSize.width, this._originalSize.height);
188     },
189 
190     /**
191      * set original size of the trimmed image
192      * @param {cc.Size} sizeInPixels
193      */
194     setOriginalSize:function (sizeInPixels) {
195         this._originalSize.width = sizeInPixels.width;
196         this._originalSize.height = sizeInPixels.height;
197     },
198 
199     /**
200      * get texture of the frame
201      * @return {cc.Texture2D}
202      */
203     getTexture:function () {
204         if (this._texture)
205             return this._texture;
206         if (this._textureFilename !== "") {
207             var locTexture = cc.TextureCache.getInstance().addImage(this._textureFilename);
208             if (locTexture)
209                 this._textureLoaded = locTexture.isLoaded();
210             return locTexture;
211         }
212         return null;
213     },
214 
215     /**
216      * set texture of the frame, the texture is retained
217      * @param {cc.Texture2D} texture
218      */
219     setTexture:function (texture) {
220         if (this._texture != texture) {
221             var locLoaded = texture.isLoaded();
222             this._textureLoaded = locLoaded;
223             this._texture = texture;
224             if(!locLoaded){
225                 texture.addLoadedEventListener(function(sender){
226                     this._textureLoaded = true;
227                     if(this._rotated){
228                         var tempElement = sender.getHtmlElementObj();
229                         tempElement = cc.cutRotateImageToCanvas(tempElement, this.getRect());
230                         var tempTexture = new cc.Texture2D();
231                         tempTexture.initWithElement(tempElement);
232                         tempTexture.handleLoadedTexture();
233                         this.setTexture(tempTexture);
234 
235                         var rect = this.getRect();
236                         this.setRect(cc.rect(0, 0, rect.width, rect.height));
237                     }
238                     var locRect = this._rect;
239                     if(locRect.width === 0 && locRect.height === 0){
240                         var locContentSize = sender.getContentSize();
241                         this._rect.width = locContentSize.width;
242                         this._rect.height = locContentSize.height;
243                         this._rectInPixels = cc.RECT_POINTS_TO_PIXELS(this._rect);
244                         this._originalSizeInPixels.width = this._rectInPixels.width;
245                         this._originalSizeInPixels.height = this._rectInPixels.height;
246                         this._originalSize.width =  locContentSize.width;
247                         this._originalSize.height =  locContentSize.height;
248                     }
249                     this._callLoadedEventCallbacks();
250                 }, this);
251             }
252         }
253     },
254 
255     /**
256      * Offset getter
257      * @const
258      * @return {cc.Point}
259      */
260     getOffset:function () {
261         return cc.p(this._offset.x, this._offset.y);
262     },
263 
264     /**
265      * offset setter
266      * @param {cc.Point} offsets
267      */
268     setOffset:function (offsets) {
269         this._offset.x = offsets.x;
270         this._offset.y = offsets.y;
271     },
272 
273     clone: function(){
274         var frame = new cc.SpriteFrame();
275         frame.initWithTextureFilename(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels);
276         frame.setTexture(this._texture);
277         return frame;
278     },
279 
280     /**
281      * copy a new SpriteFrame
282      * @return {cc.SpriteFrame}
283      */
284     copyWithZone:function () {
285         var copy = new cc.SpriteFrame();
286         copy.initWithTextureFilename(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels);
287         copy.setTexture(this._texture);
288         return copy;
289     },
290 
291     copy:function () {
292         return this.copyWithZone();
293     },
294 
295     /**
296      * Initializes SpriteFrame with Texture, rect, rotated, offset and originalSize in pixels.
297      * @param {cc.Texture2D} texture
298      * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
299      * @param {Boolean} [rotated=false]
300      * @param {cc.Point} [offset=cc.size(0,0)]
301      * @param {cc.Size} [originalSize=rect.size]
302      * @return {Boolean}
303      */
304     initWithTexture:function (texture, rect, rotated, offset, originalSize) {
305         if(arguments.length === 2)
306             rect = cc.RECT_POINTS_TO_PIXELS(rect);
307 
308         offset = offset || cc.size(0, 0);
309         originalSize = originalSize || rect.size;
310 
311         this.setTexture(texture);
312         this._rectInPixels = rect;
313         this._rect = cc.RECT_PIXELS_TO_POINTS(rect);
314         this._offsetInPixels = offset;
315         this._offset = cc.POINT_PIXELS_TO_POINTS(offset);
316         this._originalSizeInPixels = originalSize;
317         this._originalSize = cc.SIZE_PIXELS_TO_POINTS(originalSize);
318         this._rotated = rotated || false;
319         return true;
320     },
321 
322     /**
323      * <p>
324      *    Initializes a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.<br/>
325      *    The originalSize is the size in pixels of the frame before being trimmed.
326      * </p>
327      * @param {string} filename
328      * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
329      * @param {Boolean} rotated
330      * @param {cc.Point} [offset=cc.size(0,0)]
331      * @param {cc.Size} [originalSize=rect.size]
332      */
333     initWithTextureFilename:function (filename, rect, rotated, offset, originalSize) {
334         if(arguments.length === 2)
335             rect = cc.RECT_POINTS_TO_PIXELS(rect);
336 
337         offset = offset || cc.size(0, 0);
338         originalSize = originalSize || rect.size;
339 
340         this._texture = null;
341         this._textureFilename = filename;
342         this._rectInPixels = rect;
343         this._rect = cc.RECT_PIXELS_TO_POINTS(rect);
344         this._rotated = rotated || false;
345         this._offsetInPixels = offset;
346         this._offset = cc.POINT_PIXELS_TO_POINTS(offset);
347         this._originalSizeInPixels = originalSize;
348         this._originalSize = cc.SIZE_PIXELS_TO_POINTS(originalSize);
349 
350         return true;
351     }
352 });
353 
354 /**
355  * <p>
356  *    Create a cc.SpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels.<br/>
357  *    The originalSize is the size in pixels of the frame before being trimmed.
358  * </p>
359  * @param {string} filename
360  * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
361  * @param {Boolean} rotated
362  * @param {cc.Point} offset
363  * @param {cc.Size} originalSize
364  * @return {cc.SpriteFrame}
365  */
366 cc.SpriteFrame.create = function (filename, rect, rotated, offset, originalSize) {
367     var spriteFrame = new cc.SpriteFrame();
368     switch (arguments.length) {
369         case 2:
370             spriteFrame.initWithTextureFilename(filename, rect);
371             break;
372         case 5:
373             spriteFrame.initWithTextureFilename(filename, rect, rotated, offset, originalSize);
374             break;
375         default:
376             throw "Argument must be non-nil ";
377             break;
378     }
379     return spriteFrame;
380 };
381 
382 /**
383  * Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
384  * @param {cc.Texture2D} texture
385  * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
386  * @param {Boolean} [rotated=]
387  * @param {cc.Point} [offset=]
388  * @param {cc.Size} [originalSize=]
389  * @return {cc.SpriteFrame}
390  * @example
391  * //Create a cc.SpriteFrame with a texture, rect in texture.
392  * var frame1 = cc.SpriteFrame.createWithTexture("grossini_dance.png",cc.rect(0,0,90,128));
393  *
394  * //Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
395  * var frame2 = cc.SpriteFrame.createWithTexture(texture, frameRect, rotated, offset, sourceSize);
396  */
397 cc.SpriteFrame.createWithTexture = function (texture, rect, rotated, offset, originalSize) {
398     var spriteFrame = new cc.SpriteFrame();
399     switch (arguments.length) {
400         case 2:
401             spriteFrame.initWithTexture(texture, rect);
402             break;
403         case 5:
404             spriteFrame.initWithTexture(texture, rect, rotated, offset, originalSize);
405             break;
406         default:
407             throw "Argument must be non-nil ";
408             break;
409     }
410     return spriteFrame;
411 };
412 
413 cc.SpriteFrame._frameWithTextureForCanvas = function (texture, rect, rotated, offset, originalSize) {
414     var spriteFrame = new cc.SpriteFrame();
415     spriteFrame._texture = texture;
416     spriteFrame._rectInPixels = rect;
417     spriteFrame._rect = cc.RECT_PIXELS_TO_POINTS(rect);
418     spriteFrame._offsetInPixels = offset;
419     spriteFrame._offset = cc.POINT_PIXELS_TO_POINTS(spriteFrame._offsetInPixels);
420     spriteFrame._originalSizeInPixels = originalSize;
421     spriteFrame._originalSize = cc.SIZE_PIXELS_TO_POINTS(spriteFrame._originalSizeInPixels);
422     spriteFrame._rotated = rotated;
423     return spriteFrame;
424 };
425