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 * Singleton that handles the loading of the sprite frames. It saves in a cache the sprite frames. 29 * @class 30 * @extends cc.Class 31 * @example 32 * // add SpriteFrames to SpriteFrameCache With File 33 * cc.SpriteFrameCache.getInstance().addSpriteFrames(s_grossiniPlist); 34 */ 35 cc.SpriteFrameCache = cc.Class.extend(/** @lends cc.SpriteFrameCache# */{ 36 _spriteFrames:null, 37 _spriteFramesAliases:null, 38 _loadedFileNames:null, 39 40 /** 41 * Constructor 42 */ 43 ctor:function () { 44 this._spriteFrames = {}; 45 this._spriteFramesAliases = {}; 46 this._loadedFileNames = []; 47 }, 48 49 /** 50 * Adds multiple Sprite Frames with a dictionary. The texture will be associated with the created sprite frames. 51 * @param {object} dictionary 52 * @param {cc.Texture2D} texture 53 */ 54 _addSpriteFramesWithDictionary:function (dictionary, texture) { 55 var metadataDict = dictionary["metadata"]; 56 var framesDict = dictionary["frames"]; 57 var format = 0; 58 // get the format 59 if (metadataDict != null) { 60 format = parseInt(this._valueForKey("format", metadataDict)); 61 } 62 63 // check the format 64 if(format < 0 || format > 3) { 65 cc.log("format is not supported for cc.SpriteFrameCache.addSpriteFramesWithDictionary"); 66 return; 67 } 68 69 for (var key in framesDict) { 70 var frameDict = framesDict[key]; 71 if (frameDict) { 72 var spriteFrame = this._spriteFrames[key]; 73 if (spriteFrame) { 74 continue; 75 } 76 77 if (format == 0) { 78 var x = parseFloat(this._valueForKey("x", frameDict)); 79 var y = parseFloat(this._valueForKey("y", frameDict)); 80 var w = parseFloat(this._valueForKey("width", frameDict)); 81 var h = parseFloat(this._valueForKey("height", frameDict)); 82 var ox = parseFloat(this._valueForKey("offsetX", frameDict)); 83 var oy = parseFloat(this._valueForKey("offsetY", frameDict)); 84 var ow = parseInt(this._valueForKey("originalWidth", frameDict)); 85 var oh = parseInt(this._valueForKey("originalHeight", frameDict)); 86 // check ow/oh 87 if (!ow || !oh) { 88 cc.log("cocos2d: WARNING: originalWidth/Height not found on the cc.SpriteFrame. AnchorPoint won't work as expected. Regenrate the .plist"); 89 } 90 // Math.abs ow/oh 91 ow = Math.abs(ow); 92 oh = Math.abs(oh); 93 // create frame 94 spriteFrame = new cc.SpriteFrame(); 95 spriteFrame.initWithTexture(texture, cc.rect(x, y, w, h), false, cc.p(ox, oy), cc.size(ow, oh)); 96 } else if (format == 1 || format == 2) { 97 var frame = cc.RectFromString(this._valueForKey("frame", frameDict)); 98 var rotated = false; 99 100 // rotation 101 if (format == 2) { 102 rotated = this._valueForKey("rotated", frameDict) == "true"; 103 } 104 var offset = cc.PointFromString(this._valueForKey("offset", frameDict)); 105 var sourceSize = cc.SizeFromString(this._valueForKey("sourceSize", frameDict)); 106 // create frame 107 spriteFrame = new cc.SpriteFrame(); 108 spriteFrame.initWithTexture(texture, frame, rotated, offset, sourceSize); 109 } else if (format == 3) { 110 // get values 111 var spriteSize, spriteOffset, spriteSourceSize, textureRect, textureRotated; 112 spriteSize = cc.SizeFromString(this._valueForKey("spriteSize", frameDict)); 113 spriteOffset = cc.PointFromString(this._valueForKey("spriteOffset", frameDict)); 114 spriteSourceSize = cc.SizeFromString(this._valueForKey("spriteSourceSize", frameDict)); 115 textureRect = cc.RectFromString(this._valueForKey("textureRect", frameDict)); 116 textureRotated = this._valueForKey("textureRotated", frameDict) == "true"; 117 118 // get aliases 119 var aliases = frameDict["aliases"]; 120 var frameKey = key.toString(); 121 122 for (var aliasKey in aliases) { 123 if (this._spriteFramesAliases.hasOwnProperty(aliases[aliasKey])) { 124 cc.log("cocos2d: WARNING: an alias with name " + aliasKey + " already exists"); 125 } 126 this._spriteFramesAliases[aliases[aliasKey]] = frameKey; 127 } 128 129 // create frame 130 spriteFrame = new cc.SpriteFrame(); 131 if (frameDict.hasOwnProperty("spriteSize")) { 132 spriteFrame.initWithTexture(texture, 133 cc.rect(textureRect.x, textureRect.y, spriteSize.width, spriteSize.height), 134 textureRotated, 135 spriteOffset, 136 spriteSourceSize); 137 } else { 138 spriteFrame.initWithTexture(texture, spriteSize, textureRotated, spriteOffset, spriteSourceSize); 139 } 140 } 141 142 if(cc.renderContextType === cc.CANVAS && spriteFrame.isRotated()){ 143 //clip to canvas 144 var locTexture = spriteFrame.getTexture(); 145 if(locTexture.isLoaded()){ 146 var tempElement = spriteFrame.getTexture().getHtmlElementObj(); 147 tempElement = cc.cutRotateImageToCanvas(tempElement, spriteFrame.getRectInPixels()); 148 var tempTexture = new cc.Texture2D(); 149 tempTexture.initWithElement(tempElement); 150 tempTexture.handleLoadedTexture(); 151 spriteFrame.setTexture(tempTexture); 152 153 var rect = spriteFrame._rect; 154 spriteFrame.setRect(cc.rect(0, 0, rect.width, rect.height)); 155 } 156 } 157 158 // add sprite frame 159 this._spriteFrames[key] = spriteFrame; 160 } 161 } 162 }, 163 164 /** 165 * Adds multiple Sprite Frames from a json file. A texture will be loaded automatically. 166 * @param {object} jsonData 167 * @deprecated 168 */ 169 addSpriteFramesWithJson:function (jsonData) { 170 cc.log("addSpriteFramesWithJson is deprecated, because Json format doesn't support on JSB. Use XML format instead"); 171 var dict = jsonData; 172 var texturePath = ""; 173 174 var metadataDict = dict["metadata"]; 175 if (metadataDict) { 176 // try to read texture file name from meta data 177 texturePath = this._valueForKey("textureFileName", metadataDict); 178 texturePath = texturePath.toString(); 179 } 180 181 var texture = cc.TextureCache.getInstance().addImage(texturePath); 182 if (texture) { 183 this._addSpriteFramesWithDictionary(dict, texture); 184 } 185 else { 186 cc.log("cocos2d: cc.SpriteFrameCache: Couldn't load texture"); 187 } 188 }, 189 190 /** 191 * <p> 192 * Adds multiple Sprite Frames from a plist file.<br/> 193 * A texture will be loaded automatically. The texture name will composed by replacing the .plist suffix with .png<br/> 194 * If you want to use another texture, you should use the addSpriteFrames:texture method.<br/> 195 * </p> 196 * @param {String} plist plist filename 197 * @param {HTMLImageElement|cc.Texture2D|string} texture 198 * @example 199 * // add SpriteFrames to SpriteFrameCache With File 200 * cc.SpriteFrameCache.getInstance().addSpriteFrames(s_grossiniPlist); 201 */ 202 addSpriteFrames:function (plist, texture) { 203 if(!plist) 204 throw "cc.SpriteFrameCache.addSpriteFrames(): plist should be non-null"; 205 var fileUtils = cc.FileUtils.getInstance(); 206 var fullPath = fileUtils.fullPathForFilename(plist); 207 var dict = fileUtils.dictionaryWithContentsOfFileThreadSafe(fullPath); 208 209 switch (arguments.length) { 210 case 1: 211 212 if (!cc.ArrayContainsObject(this._loadedFileNames, plist)) { 213 var texturePath = ""; 214 var metadataDict = dict["metadata"]; 215 if (metadataDict) { 216 // try to read texture file name from meta data 217 texturePath = this._valueForKey("textureFileName", metadataDict).toString(); 218 } 219 if (texturePath != "") { 220 // build texture path relative to plist file 221 texturePath = fileUtils.fullPathFromRelativeFile(texturePath, plist); 222 } else { 223 // build texture path by replacing file extension 224 texturePath = plist; 225 226 // remove .xxx 227 var startPos = texturePath.lastIndexOf(".", texturePath.length); 228 texturePath = texturePath.substr(0, startPos); 229 230 // append .png 231 texturePath = texturePath + ".png"; 232 } 233 234 var getTexture = cc.TextureCache.getInstance().addImage(texturePath); 235 if (getTexture) 236 this._addSpriteFramesWithDictionary(dict, getTexture); 237 else 238 cc.log("cocos2d: cc.SpriteFrameCache: Couldn't load texture"); 239 } 240 break; 241 case 2: 242 if (texture instanceof cc.Texture2D) { 243 /** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. */ 244 this._addSpriteFramesWithDictionary(dict, texture); 245 } else { 246 /** Adds multiple Sprite Frames from a plist file. The texture will be associated with the created sprite frames. 247 @since v0.99.5 248 */ 249 var textureFileName = texture; 250 if(!textureFileName) 251 throw "cc.SpriteFrameCache.addSpriteFrames(): texture name should not be null"; 252 var gTexture = cc.TextureCache.getInstance().addImage(textureFileName); 253 254 if (gTexture) { 255 this._addSpriteFramesWithDictionary(dict, gTexture); 256 } else { 257 cc.log("cocos2d: cc.SpriteFrameCache: couldn't load texture file. File not found " + textureFileName); 258 } 259 } 260 break; 261 default: 262 throw "Argument must be non-nil "; 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 this._loadedFileNames = {}; 291 }, 292 293 /** 294 * Deletes an sprite frame from the sprite frame cache. 295 * @param {String} name 296 */ 297 removeSpriteFrameByName:function (name) { 298 // explicit nil handling 299 if (!name) { 300 return; 301 } 302 303 // Is this an alias ? 304 if (this._spriteFramesAliases.hasOwnProperty(name)) { 305 delete(this._spriteFramesAliases[name]); 306 } 307 if (this._spriteFrames.hasOwnProperty(name)) { 308 delete(this._spriteFrames[name]); 309 } 310 // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache 311 this._loadedFileNames = {}; 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} plist plist filename 321 */ 322 removeSpriteFramesFromFile:function (plist) { 323 var fileUtils = cc.FileUtils.getInstance(); 324 var path = fileUtils.fullPathForFilename(plist); 325 var dict = fileUtils.dictionaryWithContentsOfFileThreadSafe(path); 326 327 this._removeSpriteFramesFromDictionary(dict); 328 329 //remove it from the cache 330 if (cc.ArrayContainsObject(this._loadedFileNames, plist)) { 331 cc.ArrayRemoveObject(plist); 332 } 333 }, 334 335 /** 336 * Removes multiple Sprite Frames from Dictionary. 337 * @param {object} dictionary SpriteFrame of Dictionary 338 */ 339 _removeSpriteFramesFromDictionary:function (dictionary) { 340 var framesDict = dictionary["frames"]; 341 342 for (var key in framesDict) { 343 if (this._spriteFrames.hasOwnProperty(key)) { 344 delete(this._spriteFrames[key]); 345 } 346 } 347 }, 348 349 /** 350 * <p> 351 * Removes all Sprite Frames associated with the specified textures.<br/> 352 * It is convinient to call this method when a specific texture needs to be removed. 353 * </p> 354 * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture 355 */ 356 removeSpriteFramesFromTexture:function (texture) { 357 for (var key in this._spriteFrames) { 358 var frame = this._spriteFrames[key]; 359 if (frame && (frame.getTexture() == texture)) { 360 delete(this._spriteFrames[key]); 361 } 362 } 363 }, 364 365 /** 366 * <p> 367 * Returns an Sprite Frame that was previously added.<br/> 368 * If the name is not found it will return nil.<br/> 369 * You should retain the returned copy if you are going to use it.<br/> 370 * </p> 371 * @param {String} name name of SpriteFrame 372 * @return {cc.SpriteFrame} 373 * @example 374 * //get a SpriteFrame by name 375 * var frame = cc.SpriteFrameCache.getInstance().getSpriteFrame("grossini_dance_01.png"); 376 */ 377 getSpriteFrame:function (name) { 378 var frame; 379 if (this._spriteFrames.hasOwnProperty(name)) { 380 frame = this._spriteFrames[name]; 381 } 382 383 if (!frame) { 384 // try alias dictionary 385 var key; 386 if (this._spriteFramesAliases.hasOwnProperty(name)) { 387 key = this._spriteFramesAliases[name]; 388 } 389 if (key) { 390 if (this._spriteFrames.hasOwnProperty(key.toString())) { 391 frame = this._spriteFrames[key.toString()]; 392 } 393 if (!frame) { 394 cc.log("cocos2d: cc.SpriteFrameCahce: Frame " + name + " not found"); 395 } 396 } 397 } 398 return frame; 399 }, 400 401 _valueForKey:function (key, dict) { 402 if (dict) { 403 if (dict.hasOwnProperty(key)) { 404 return dict[key].toString(); 405 } 406 } 407 return ""; 408 } 409 }); 410 411 cc.s_sharedSpriteFrameCache = null; 412 413 /** 414 * Returns the shared instance of the Sprite Frame cache 415 * @return {cc.SpriteFrameCache} 416 */ 417 cc.SpriteFrameCache.getInstance = function () { 418 if (!cc.s_sharedSpriteFrameCache) { 419 cc.s_sharedSpriteFrameCache = new cc.SpriteFrameCache(); 420 } 421 return cc.s_sharedSpriteFrameCache; 422 }; 423 424 /** 425 * Purges the cache. It releases all the Sprite Frames and the retained instance. 426 */ 427 cc.SpriteFrameCache.purgeSharedSpriteFrameCache = function () { 428 cc.s_sharedSpriteFrameCache = null; 429 }; 430