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