1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga 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 if(!dict) throw "Please load the resource first : " + url; 111 cc.loader.release(url);//release it in loader 112 if(dict._inited){ 113 this._frameConfigCache[url] = dict; 114 return dict; 115 } 116 var tempFrames = dict["frames"], tempMeta = dict["metadata"] || dict["meta"]; 117 var frames = {}, meta = {}; 118 var format = 0; 119 if(tempMeta){//init meta 120 var tmpFormat = tempMeta["format"]; 121 format = (tmpFormat.length <= 1) ? parseInt(tmpFormat) : tmpFormat; 122 meta.image = tempMeta["textureFileName"] || tempMeta["textureFileName"] || tempMeta["image"]; 123 } 124 for (var key in tempFrames) { 125 var frameDict = tempFrames[key]; 126 if(!frameDict) continue; 127 var tempFrame = {}; 128 129 if (format == 0) { 130 tempFrame.rect = cc.rect(frameDict["x"], frameDict["y"], frameDict["width"], frameDict["height"]); 131 tempFrame.rotated = false; 132 tempFrame.offset = cc.p(frameDict["offsetX"], frameDict["offsetY"]); 133 var ow = frameDict["originalWidth"]; 134 var oh = frameDict["originalHeight"]; 135 // check ow/oh 136 if (!ow || !oh) { 137 cc.log("cocos2d: WARNING: originalWidth/Height not found on the cc.SpriteFrame. AnchorPoint won't work as expected. Regenrate the .plist"); 138 } 139 // Math.abs ow/oh 140 ow = Math.abs(ow); 141 oh = Math.abs(oh); 142 tempFrame.size = cc.size(ow, oh); 143 } else if (format == 1 || format == 2) { 144 tempFrame.rect = this._rectFromString(frameDict["frame"]); 145 tempFrame.rotated = frameDict["rotated"] || false; 146 tempFrame.offset = this._pointFromString(frameDict["offset"]); 147 tempFrame.size = this._sizeFromString(frameDict["sourceSize"]); 148 } else if (format == 3) { 149 // get values 150 var spriteSize = this._sizeFromString(frameDict["spriteSize"]); 151 var textureRect = this._rectFromString(frameDict["textureRect"]); 152 if (spriteSize) { 153 textureRect = cc.rect(textureRect.x, textureRect.y, spriteSize.width, spriteSize.height); 154 } 155 tempFrame.rect = textureRect; 156 tempFrame.rotated = frameDict["textureRotated"] || false; // == "true"; 157 tempFrame.offset = this._pointFromString(frameDict["spriteOffset"]); 158 tempFrame.size = this._sizeFromString(frameDict["spriteSourceSize"]); 159 tempFrame.aliases = frameDict["aliases"]; 160 } else { 161 var tmpFrame = frameDict["frame"], tmpSourceSize = frameDict["sourceSize"]; 162 key = frameDict["filename"] || key; 163 tempFrame.rect = cc.rect(tmpFrame["x"], tmpFrame["y"], tmpFrame["w"], tmpFrame["h"]); 164 tempFrame.rotated = frameDict["rotated"] || false; 165 tempFrame.offset = cc.p(0, 0); 166 tempFrame.size = cc.size(tmpSourceSize["w"], tmpSourceSize["h"]); 167 } 168 frames[key] = tempFrame; 169 } 170 var cfg = this._frameConfigCache[url] = { 171 _inited : true, 172 frames : frames, 173 meta : meta 174 }; 175 return cfg; 176 }, 177 178 /** 179 * <p> 180 * Adds multiple Sprite Frames from a plist or json file.<br/> 181 * A texture will be loaded automatically. The texture name will composed by replacing the .plist or .json suffix with .png<br/> 182 * If you want to use another texture, you should use the addSpriteFrames:texture method.<br/> 183 * </p> 184 * @param {String} url file path 185 * @param {HTMLImageElement|cc.Texture2D|string} texture 186 * @example 187 * // add SpriteFrames to SpriteFrameCache With File 188 * cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist); 189 * cc.spriteFrameCache.addSpriteFrames(s_grossiniJson); 190 */ 191 addSpriteFrames: function (url, texture) { 192 if (!url) 193 throw "cc.SpriteFrameCache.addSpriteFrames(): plist should be non-null"; 194 195 var self = this; 196 var frameConfig = self._frameConfigCache[url] || self._getFrameConfig(url); 197 //self._checkConflict(frameConfig); //TODO 198 var frames = frameConfig.frames, meta = frameConfig.meta; 199 if(!texture){ 200 var texturePath = cc.path.changeBasename(url, meta.image || ".png"); 201 texture = cc.textureCache.addImage(texturePath); 202 }else if(texture instanceof cc.Texture2D){ 203 //do nothing 204 }else if(typeof texture == "string"){//string 205 texture = cc.textureCache.addImage(texture); 206 }else throw "Argument must be non-nil" 207 208 //create sprite frames 209 var spAliases = self._spriteFramesAliases, spriteFrames = self._spriteFrames; 210 for (var key in frames) { 211 var frame = frames[key]; 212 var spriteFrame = spriteFrames[key]; 213 if (!spriteFrame) { 214 spriteFrame = cc.SpriteFrame.create(texture, frame.rect, frame.rotated, frame.offset, frame.size); 215 var aliases = frame.aliases; 216 if(aliases){//set aliases 217 for(var i = 0, li = aliases.length; i < li; i++){ 218 var alias = aliases[i]; 219 if (spAliases[alias]) { 220 cc.log("cocos2d: WARNING: an alias with name " + alias + " already exists"); 221 } 222 spAliases[alias] = key; 223 } 224 } 225 if (cc._renderType === cc._RENDER_TYPE_CANVAS && spriteFrame.isRotated()) { 226 //clip to canvas 227 var locTexture = spriteFrame.getTexture(); 228 if (locTexture.isLoaded()) { 229 var tempElement = spriteFrame.getTexture().getHtmlElementObj(); 230 tempElement = cc.cutRotateImageToCanvas(tempElement, spriteFrame.getRectInPixels()); 231 var tempTexture = new cc.Texture2D(); 232 tempTexture.initWithElement(tempElement); 233 tempTexture.handleLoadedTexture(); 234 spriteFrame.setTexture(tempTexture); 235 236 var rect = spriteFrame._rect; 237 spriteFrame.setRect(cc.rect(0, 0, rect.width, rect.height)); 238 } 239 } 240 spriteFrames[key] = spriteFrame; 241 } 242 } 243 }, 244 245 // Function to check if frames to add exists already, if so there may be name conflit that must be solved 246 _checkConflict: function (dictionary) { 247 var framesDict = dictionary["frames"]; 248 249 for (var key in framesDict) { 250 if (this._spriteFrames[key]) { 251 cc.log("cocos2d: WARNING: Sprite frame: "+key+" has already been added by another source, please fix name conflit"); 252 } 253 } 254 }, 255 256 /** 257 * <p> 258 * Adds an sprite frame with a given name.<br/> 259 * If the name already exists, then the contents of the old name will be replaced with the new one. 260 * </p> 261 * @param {cc.SpriteFrame} frame 262 * @param {String} frameName 263 */ 264 addSpriteFrame: function (frame, frameName) { 265 this._spriteFrames[frameName] = frame; 266 }, 267 268 /** 269 * <p> 270 * Purges the dictionary of loaded sprite frames.<br/> 271 * Call this method if you receive the "Memory Warning".<br/> 272 * In the short term: it will free some resources preventing your app from being killed.<br/> 273 * In the medium term: it will allocate more resources.<br/> 274 * In the long term: it will be the same.<br/> 275 * </p> 276 */ 277 removeSpriteFrames: function () { 278 this._spriteFrames = {}; 279 this._spriteFramesAliases = {}; 280 }, 281 282 /** 283 * Deletes an sprite frame from the sprite frame cache. 284 * @param {String} name 285 */ 286 removeSpriteFrameByName: function (name) { 287 // explicit nil handling 288 if (!name) { 289 return; 290 } 291 292 // Is this an alias ? 293 if (this._spriteFramesAliases[name]) { 294 delete(this._spriteFramesAliases[name]); 295 } 296 if (this._spriteFrames[name]) { 297 delete(this._spriteFrames[name]); 298 } 299 // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache 300 }, 301 302 /** 303 * <p> 304 * Removes multiple Sprite Frames from a plist file.<br/> 305 * Sprite Frames stored in this file will be removed.<br/> 306 * It is convinient to call this method when a specific texture needs to be removed.<br/> 307 * </p> 308 * @param {String} url plist filename 309 */ 310 removeSpriteFramesFromFile: function (url) { 311 var self = this, spriteFrames = self._spriteFrames, 312 aliases = self._spriteFramesAliases, cfg = self._frameConfigCache[url]; 313 if(!cfg) return; 314 var frames = cfg.frames; 315 for (var key in frames) { 316 if (spriteFrames[key]) { 317 delete(spriteFrames[key]); 318 for (var alias in aliases) {//remove alias 319 if(aliases[alias] == key) delete aliases[alias]; 320 } 321 } 322 } 323 }, 324 325 /** 326 * <p> 327 * Removes all Sprite Frames associated with the specified textures.<br/> 328 * It is convinient to call this method when a specific texture needs to be removed. 329 * </p> 330 * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture 331 */ 332 removeSpriteFramesFromTexture: function (texture) { 333 var self = this, spriteFrames = self._spriteFrames, aliases = self._spriteFramesAliases; 334 for (var key in spriteFrames) { 335 var frame = spriteFrames[key]; 336 if (frame && (frame.getTexture() == texture)) { 337 delete(spriteFrames[key]); 338 for (var alias in aliases) {//remove alias 339 if(aliases[alias] == key) delete aliases[alias]; 340 } 341 } 342 } 343 }, 344 345 /** 346 * <p> 347 * Returns an Sprite Frame that was previously added.<br/> 348 * If the name is not found it will return nil.<br/> 349 * You should retain the returned copy if you are going to use it.<br/> 350 * </p> 351 * @param {String} name name of SpriteFrame 352 * @return {cc.SpriteFrame} 353 * @example 354 * //get a SpriteFrame by name 355 * var frame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); 356 */ 357 getSpriteFrame: function (name) { 358 var self = this, frame = self._spriteFrames[name]; 359 if (!frame) { 360 // try alias dictionary 361 var key = self._spriteFramesAliases[name]; 362 if (key) { 363 frame = self._spriteFrames[key.toString()]; 364 if(!frame) delete self._spriteFramesAliases[name]; 365 } 366 } 367 if (!frame) cc.log("cocos2d: cc.SpriteFrameCahce: Frame " + name + " not found"); 368 return frame; 369 }, 370 371 _clear: function () { 372 this._spriteFrames = {}; 373 this._spriteFramesAliases = {}; 374 this._frameConfigCache = {}; 375 } 376 }; 377