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