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