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  * cc.spriteFrameCache is a singleton that handles the loading of the sprite frames. It saves in a cache the sprite frames.<br/>
 30  * <br/>
 31  * example<br/>
 32  * // add SpriteFrames to spriteFrameCache With File<br/>
 33  * cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist);<br/>
 34  * </p>
 35  * @class
 36  * @name cc.spriteFrameCache
 37  */
 38 cc.spriteFrameCache = /** @lends cc.spriteFrameCache# */{
 39     _CCNS_REG1 : /^\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*$/,
 40     _CCNS_REG2 : /^\s*\{\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*,\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*\}\s*$/,
 41 
 42     _spriteFrames: {},
 43     _spriteFramesAliases: {},
 44     _frameConfigCache : {},
 45 
 46     _rectFromString :  function (content) {
 47         var result = this._CCNS_REG2.exec(content);
 48         if(!result) return cc.rect(0, 0, 0, 0);
 49         return cc.rect(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4]));
 50     },
 51 
 52     _pointFromString : function (content) {
 53         var result = this._CCNS_REG1.exec(content);
 54         if(!result) return cc.p(0,0);
 55         return cc.p(parseFloat(result[1]), parseFloat(result[2]));
 56     },
 57 
 58     _sizeFromString : function (content) {
 59         var result = this._CCNS_REG1.exec(content);
 60         if(!result) return cc.size(0, 0);
 61         return cc.size(parseFloat(result[1]), parseFloat(result[2]));
 62     },
 63 
 64     _getFrameConfig : function(url){
 65         var dict = cc.loader.getRes(url);
 66 
 67         cc.assert(dict, cc._LogInfos.spriteFrameCache__getFrameConfig_2, url);
 68 
 69         cc.loader.release(url);//release it in loader
 70         if(dict._inited){
 71             this._frameConfigCache[url] = dict;
 72             return dict;
 73         }
 74         var tempFrames = dict["frames"], tempMeta = dict["metadata"] || dict["meta"];
 75         var frames = {}, meta = {};
 76         var format = 0;
 77         if(tempMeta){//init meta
 78             var tmpFormat = tempMeta["format"];
 79             format = (tmpFormat.length <= 1) ? parseInt(tmpFormat) : tmpFormat;
 80             meta.image = tempMeta["textureFileName"] || tempMeta["textureFileName"] || tempMeta["image"];
 81         }
 82         for (var key in tempFrames) {
 83             var frameDict = tempFrames[key];
 84             if(!frameDict) continue;
 85             var tempFrame = {};
 86 
 87             if (format == 0) {
 88                 tempFrame.rect = cc.rect(frameDict["x"], frameDict["y"], frameDict["width"], frameDict["height"]);
 89                 tempFrame.rotated = false;
 90                 tempFrame.offset = cc.p(frameDict["offsetX"], frameDict["offsetY"]);
 91                 var ow = frameDict["originalWidth"];
 92                 var oh = frameDict["originalHeight"];
 93                 // check ow/oh
 94                 if (!ow || !oh) {
 95                     cc.log(cc._LogInfos.spriteFrameCache__getFrameConfig);
 96                 }
 97                 // Math.abs ow/oh
 98                 ow = Math.abs(ow);
 99                 oh = Math.abs(oh);
100                 tempFrame.size = cc.size(ow, oh);
101             } else if (format == 1 || format == 2) {
102                 tempFrame.rect = this._rectFromString(frameDict["frame"]);
103                 tempFrame.rotated = frameDict["rotated"] || false;
104                 tempFrame.offset = this._pointFromString(frameDict["offset"]);
105                 tempFrame.size = this._sizeFromString(frameDict["sourceSize"]);
106             } else if (format == 3) {
107                 // get values
108                 var spriteSize = this._sizeFromString(frameDict["spriteSize"]);
109                 var textureRect = this._rectFromString(frameDict["textureRect"]);
110                 if (spriteSize) {
111                     textureRect = cc.rect(textureRect.x, textureRect.y, spriteSize.width, spriteSize.height);
112                 }
113                 tempFrame.rect = textureRect;
114                 tempFrame.rotated = frameDict["textureRotated"] || false; // == "true";
115                 tempFrame.offset = this._pointFromString(frameDict["spriteOffset"]);
116                 tempFrame.size = this._sizeFromString(frameDict["spriteSourceSize"]);
117                 tempFrame.aliases = frameDict["aliases"];
118             } else {
119                 var tmpFrame = frameDict["frame"], tmpSourceSize = frameDict["sourceSize"];
120                 key = frameDict["filename"] || key;
121                 tempFrame.rect = cc.rect(tmpFrame["x"], tmpFrame["y"], tmpFrame["w"], tmpFrame["h"]);
122                 tempFrame.rotated = frameDict["rotated"] || false;
123                 tempFrame.offset = cc.p(0, 0);
124                 tempFrame.size = cc.size(tmpSourceSize["w"], tmpSourceSize["h"]);
125             }
126             frames[key] = tempFrame;
127         }
128         var cfg = this._frameConfigCache[url] = {
129             _inited : true,
130             frames : frames,
131             meta : meta
132         };
133         return cfg;
134     },
135 
136     /**
137      * <p>
138      *   Adds multiple Sprite Frames from a plist or json file.<br/>
139      *   A texture will be loaded automatically. The texture name will composed by replacing the .plist or .json suffix with .png<br/>
140      *   If you want to use another texture, you should use the addSpriteFrames:texture method.<br/>
141      * </p>
142      * @param {String} url file path
143      * @param {HTMLImageElement|cc.Texture2D|string} texture
144      * @example
145      * // add SpriteFrames to SpriteFrameCache With File
146      * cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist);
147      * cc.spriteFrameCache.addSpriteFrames(s_grossiniJson);
148      */
149     addSpriteFrames: function (url, texture) {
150         cc.assert(url, cc._LogInfos.spriteFrameCache_addSpriteFrames_2);
151 
152         //Is it a SpriteFrame plist?
153         var dict = this._frameConfigCache[url] || cc.loader.getRes(url);
154         if(!dict || !dict["frames"])
155             return;
156 
157         var self = this;
158         var frameConfig = self._frameConfigCache[url] || self._getFrameConfig(url);
159         //self._checkConflict(frameConfig);                             //TODO
160         var frames = frameConfig.frames, meta = frameConfig.meta;
161         if(!texture){
162             var texturePath = cc.path.changeBasename(url, meta.image || ".png");
163             texture = cc.textureCache.addImage(texturePath);
164         }else if(texture instanceof cc.Texture2D){
165             //do nothing
166         }else if(typeof texture == "string"){//string
167             texture = cc.textureCache.addImage(texture);
168         }else{
169             cc.assert(0, cc._LogInfos.spriteFrameCache_addSpriteFrames_3);
170         }
171 
172         //create sprite frames
173         var spAliases = self._spriteFramesAliases, spriteFrames = self._spriteFrames;
174         for (var key in frames) {
175             var frame = frames[key];
176             var spriteFrame = spriteFrames[key];
177             if (!spriteFrame) {
178                 spriteFrame = cc.SpriteFrame.create(texture, frame.rect, frame.rotated, frame.offset, frame.size);
179                 var aliases = frame.aliases;
180                 if(aliases){//set aliases
181                     for(var i = 0, li = aliases.length; i < li; i++){
182                         var alias = aliases[i];
183                         if (spAliases[alias]) {
184                             cc.log(cc._LogInfos.spriteFrameCache_addSpriteFrames, alias);
185                         }
186                         spAliases[alias] = key;
187                     }
188                 }
189 
190                 if (cc._renderType === cc._RENDER_TYPE_CANVAS && spriteFrame.isRotated()) {
191                     //clip to canvas
192                     var locTexture = spriteFrame.getTexture();
193                     if (locTexture.isLoaded()) {
194                         var tempElement = spriteFrame.getTexture().getHtmlElementObj();
195                         tempElement = cc.cutRotateImageToCanvas(tempElement, spriteFrame.getRectInPixels());
196                         var tempTexture = new cc.Texture2D();
197                         tempTexture.initWithElement(tempElement);
198                         tempTexture.handleLoadedTexture();
199                         spriteFrame.setTexture(tempTexture);
200 
201                         var rect = spriteFrame._rect;
202                         spriteFrame.setRect(cc.rect(0, 0, rect.width, rect.height));
203                     }
204                 }
205 
206                 spriteFrames[key] = spriteFrame;
207             }
208         }
209     },
210 
211     // Function to check if frames to add exists already, if so there may be name conflit that must be solved
212     _checkConflict: function (dictionary) {
213         var framesDict = dictionary["frames"];
214 
215         for (var key in framesDict) {
216             if (this._spriteFrames[key]) {
217                 cc.log(cc._LogInfos.spriteFrameCache__checkConflict, key);
218             }
219         }
220     },
221 
222     /**
223      * <p>
224      *  Adds an sprite frame with a given name.<br/>
225      *  If the name already exists, then the contents of the old name will be replaced with the new one.
226      * </p>
227      * @param {cc.SpriteFrame} frame
228      * @param {String} frameName
229      */
230     addSpriteFrame: function (frame, frameName) {
231         this._spriteFrames[frameName] = frame;
232     },
233 
234     /**
235      * <p>
236      *   Purges the dictionary of loaded sprite frames.<br/>
237      *   Call this method if you receive the "Memory Warning".<br/>
238      *   In the short term: it will free some resources preventing your app from being killed.<br/>
239      *   In the medium term: it will allocate more resources.<br/>
240      *   In the long term: it will be the same.<br/>
241      * </p>
242      */
243     removeSpriteFrames: function () {
244         this._spriteFrames = {};
245         this._spriteFramesAliases = {};
246     },
247 
248     /**
249      * Deletes an sprite frame from the sprite frame cache.
250      * @param {String} name
251      */
252     removeSpriteFrameByName: function (name) {
253         // explicit nil handling
254         if (!name) {
255             return;
256         }
257 
258         // Is this an alias ?
259         if (this._spriteFramesAliases[name]) {
260             delete(this._spriteFramesAliases[name]);
261         }
262         if (this._spriteFrames[name]) {
263             delete(this._spriteFrames[name]);
264         }
265         // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache
266     },
267 
268     /**
269      * <p>
270      *     Removes multiple Sprite Frames from a plist file.<br/>
271      *     Sprite Frames stored in this file will be removed.<br/>
272      *     It is convinient to call this method when a specific texture needs to be removed.<br/>
273      * </p>
274      * @param {String} url Plist filename
275      */
276     removeSpriteFramesFromFile: function (url) {
277         var self = this, spriteFrames = self._spriteFrames,
278             aliases = self._spriteFramesAliases, cfg = self._frameConfigCache[url];
279         if(!cfg) return;
280         var frames = cfg.frames;
281         for (var key in frames) {
282             if (spriteFrames[key]) {
283                 delete(spriteFrames[key]);
284                 for (var alias in aliases) {//remove alias
285                     if(aliases[alias] == key) delete aliases[alias];
286                 }
287             }
288         }
289     },
290 
291     /**
292      * <p>
293      *    Removes all Sprite Frames associated with the specified textures.<br/>
294      *    It is convenient to call this method when a specific texture needs to be removed.
295      * </p>
296      * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture
297      */
298     removeSpriteFramesFromTexture: function (texture) {
299         var self = this, spriteFrames = self._spriteFrames, aliases = self._spriteFramesAliases;
300         for (var key in spriteFrames) {
301             var frame = spriteFrames[key];
302             if (frame && (frame.getTexture() == texture)) {
303                 delete(spriteFrames[key]);
304                 for (var alias in aliases) {//remove alias
305                     if(aliases[alias] == key) delete aliases[alias];
306                 }
307             }
308         }
309     },
310 
311     /**
312      * <p>
313      *   Returns an Sprite Frame that was previously added.<br/>
314      *   If the name is not found it will return nil.<br/>
315      *   You should retain the returned copy if you are going to use it.<br/>
316      * </p>
317      * @param {String} name name of SpriteFrame
318      * @return {cc.SpriteFrame}
319      * @example
320      * //get a SpriteFrame by name
321      * var frame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png");
322      */
323     getSpriteFrame: function (name) {
324         var self = this, frame = self._spriteFrames[name];
325         if (!frame) {
326             // try alias dictionary
327             var key = self._spriteFramesAliases[name];
328             if (key) {
329                 frame = self._spriteFrames[key.toString()];
330                 if(!frame) delete self._spriteFramesAliases[name];
331             }
332         }
333         if (!frame) cc.log(cc._LogInfos.spriteFrameCache_getSpriteFrame, name);
334         return frame;
335     },
336 
337 	_clear: function () {
338 		this._spriteFrames = {};
339 		this._spriteFramesAliases = {};
340 		this._frameConfigCache = {};
341 	}
342 };
343