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