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     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             selCallback.eventCallback.call(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 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         cc._POINT_PIXELS_TO_POINTS_OUT(this._offsetInPixels, this._offset);
168     },
169 
170     /**
171      * get original size of the trimmed image
172      * @const
173      * @return {cc.Size}
174      */
175     getOriginalSizeInPixels:function () {
176         return 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 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.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._renderType === cc._RENDER_TYPE_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 w = sender.width, h = sender.height;
248                         this._rect.width = w;
249                         this._rect.height = h;
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 =  w;
254                         this._originalSize.height =  h;
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 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.initWithTexture(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.initWithTexture(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 {String|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;
317         rotated = rotated || false;
318 
319         if (typeof(texture) == "string"){
320             this._texture = null;
321             this._textureFilename = texture;
322         } else if (texture instanceof cc.Texture2D){
323             this.setTexture(texture);
324         }
325 
326         this._rectInPixels = rect;
327         this._rect = cc.RECT_PIXELS_TO_POINTS(rect);
328         this._offsetInPixels.x = offset.x;
329         this._offsetInPixels.y = offset.y;
330         cc._POINT_PIXELS_TO_POINTS_OUT(offset, this._offset);
331         this._originalSizeInPixels.width = originalSize.width;
332         this._originalSizeInPixels.height = originalSize.height;
333         cc._SIZE_PIXELS_TO_POINTS_OUT(originalSize, this._originalSize);
334         this._rotated = rotated;
335         return true;
336     }
337 });
338 
339 /**
340  * <p>
341  *    Create a cc.SpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels.<br/>
342  *    The originalSize is the size in pixels of the frame before being trimmed.
343  * </p>
344  * @param {String|cc.Texture2D} filename
345  * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels
346  * @param {Boolean} rotated
347  * @param {cc.Point} offset
348  * @param {cc.Size} originalSize
349  * @return {cc.SpriteFrame}
350  * @example
351  * 1.
352  * //Create a cc.SpriteFrame with image path
353  * var frame1 = cc.SpriteFrame.create("res/grossini_dance.png",cc.rect(0,0,90,128));
354  * var frame2 = cc.SpriteFrame.create("res/grossini_dance.png",cc.rect(0,0,90,128),false,0,cc.size(90,128));
355  *
356  * 2.
357  * //Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels.
358  * var texture = cc.textureCache.addImage("res/grossini_dance.png");
359  * var frame1 = cc.SpriteFrame.create(texture, cc.rect(0,0,90,128));
360  * var frame2 = cc.SpriteFrame.create(texture, cc.rect(0,0,90,128),false,0,cc.size(90,128));
361  */
362 cc.SpriteFrame.create = function (filename, rect, rotated, offset, originalSize) {
363     var spriteFrame = new cc.SpriteFrame();
364     switch (arguments.length) {
365         case 2:
366             spriteFrame.initWithTexture(filename, rect);
367             break;
368         case 5:
369             spriteFrame.initWithTexture(filename, rect, rotated, offset, originalSize);
370             break;
371         default:
372             throw "Argument must be non-nil ";
373             break;
374     }
375     return spriteFrame;
376 };
377 
378 cc.SpriteFrame._frameWithTextureForCanvas = function (texture, rect, rotated, offset, originalSize) {
379     var spriteFrame = new cc.SpriteFrame();
380     spriteFrame._texture = texture;
381     spriteFrame._rectInPixels = rect;
382     spriteFrame._rect = cc.RECT_PIXELS_TO_POINTS(rect);
383     spriteFrame._offsetInPixels.x = offset.x;
384     spriteFrame._offsetInPixels.y = offset.y;
385     cc._POINT_PIXELS_TO_POINTS_OUT(spriteFrame._offsetInPixels, spriteFrame._offset);
386     spriteFrame._originalSizeInPixels.width = originalSize.width;
387     spriteFrame._originalSizeInPixels.height = originalSize.height;
388     cc._SIZE_PIXELS_TO_POINTS_OUT(spriteFrame._originalSizeInPixels, spriteFrame._originalSize);
389     spriteFrame._rotated = rotated;
390     return spriteFrame;
391 };
392