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