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