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 * TextureCache - Alloc, Init & Dealloc 29 * @type object 30 */ 31 cc.g_sharedTextureCache = null; 32 33 /** 34 * Load the images to the cache 35 * @param {String} imageUrl 36 */ 37 cc.loadImage = function (imageUrl) { 38 // compute image type 39 var imageType = cc.computeImageFormatType(imageUrl); 40 if (imageType == cc.FMT_UNKNOWN) { 41 cc.log("unsupported format" + imageUrl); 42 return; 43 } 44 var image = new Image(); 45 image.src = imageUrl; 46 image.onLoad = function (e) { 47 cc.TextureCache.getInstance().cacheImage(imageUrl, image); 48 }; 49 }; 50 51 /** 52 * Support image format type 53 * @param {String} filename 54 * @return {Number} 55 */ 56 cc.computeImageFormatType = function (filename) { 57 if (filename.toLowerCase().indexOf('.jpg') > 0 || filename.toLowerCase().indexOf('.jpeg') > 0) { 58 return cc.FMT_JPG; 59 } else if (filename.indexOf('.png') > 0 || filename.indexOf('.PNG') > 0) { 60 return cc.FMT_PNG; 61 } 62 return cc.FMT_UNKNOWN; 63 }; 64 65 /** 66 * Implementation TextureCache 67 * @class 68 * @extends cc.Class 69 */ 70 cc.TextureCache = cc.Class.extend(/** @lends cc.TextureCache# */{ 71 textures:{}, 72 _textureColorsCache:{}, 73 _textureKeySeq:1000, 74 75 /** 76 * Constructor 77 */ 78 ctor:function () { 79 cc.Assert(cc.g_sharedTextureCache == null, "Attempted to allocate a second instance of a singleton."); 80 this._textureKeySeq += (0|Math.random() * 1000); 81 }, 82 83 /** 84 * Loading the images asynchronously 85 * @param {String} path 86 * @param {cc.Node} target 87 * @param {Function} selector 88 * @return {Image} 89 * @example 90 * //example 91 * cc.TextureCache.getInstance().addImageAsync("hello.png", this, this.loadingCallBack); 92 */ 93 addImageAsync:function (path, target, selector) { 94 cc.Assert(path != null, "TextureCache: path MUST not be null"); 95 96 path = cc.FileUtils.getInstance().fullPathFromRelativePath(path); 97 98 var texture = this.textures[path.toString()]; 99 100 if (texture) { 101 this._addImageAsyncCallBack(target, selector); 102 } 103 else { 104 texture = new Image(); 105 var that = this; 106 texture.addEventListener("load", function () { 107 that._addImageAsyncCallBack(target, selector); 108 }); 109 texture.src = path; 110 this.textures[path.toString()] = texture; 111 } 112 113 if (cc.renderContextType == cc.CANVAS) { 114 return this.textures[path.toString()]; 115 } else { 116 //todo texure for gl 117 } 118 }, 119 _addImageAsyncCallBack:function (target, selector) { 120 if (target && (typeof(selector) == "string")) { 121 target[selector](); 122 } else if (target && (typeof(selector) == "function")) { 123 selector.call(target); 124 } 125 }, 126 127 /** 128 * AddPVRTCImage does not support 129 */ 130 addPVRTCImage:function () { 131 cc.Assert(0, "TextureCache:addPVRTCImage does not support"); 132 }, 133 134 /** 135 * Description 136 * @return {String} 137 */ 138 description:function () { 139 return "<TextureCache | Number of textures = " + this.textures.length + ">"; 140 }, 141 142 /** 143 * <p>Returns a Texture2D object given an file image <br /> 144 * If the file image was not previously loaded, it will create a new Texture2D <br /> 145 * object and it will return it. It will use the filename as a key.<br /> 146 * Otherwise it will return a reference of a previously loaded image. <br /> 147 * Supported image extensions: .png, .jpg, .gif</p> 148 * @param {String} path 149 * @return {Image} 150 * @example 151 * //example 152 * cc.TextureCache.getInstance().addImage("hello.png"); 153 */ 154 addImage:function (path) { 155 cc.Assert(path != null, "TextureCache: path MUST not be null"); 156 157 path = cc.FileUtils.getInstance().fullPathFromRelativePath(path); 158 159 var texture = this.textures[path.toString()]; 160 if (texture) { 161 cc.Loader.getInstance().onResLoaded(); 162 } 163 else { 164 texture = new Image(); 165 var that = this; 166 texture.addEventListener("load", function () { 167 168 cc.Loader.getInstance().onResLoaded(); 169 }); 170 texture.addEventListener("error", function () { 171 cc.Loader.getInstance().onResLoadingErr(path); 172 }); 173 texture.src = path; 174 this.textures[path.toString()] = texture; 175 } 176 177 if (cc.renderContextType == cc.CANVAS) { 178 return this.textures[path.toString()]; 179 } else { 180 //todo texture for gl 181 } 182 }, 183 184 /** 185 * Cache the image data 186 * @param {String} path 187 * @param {Image} texture 188 */ 189 cacheImage:function (path, texture) { 190 if (!this.textures[path.toString()]) { 191 this.textures[path.toString()] = texture; 192 } 193 }, 194 195 /** 196 * <p>Returns a Texture2D object given an UIImage image<br /> 197 * If the image was not previously loaded, it will create a new Texture2D object and it will return it.<br /> 198 * Otherwise it will return a reference of a previously loaded image<br /> 199 * The "key" parameter will be used as the "key" for the cache.<br /> 200 * If "key" is null, then a new texture will be created each time.</p> 201 * @param {Image} image 202 * @param {String} key 203 * @return {cc.Texture2D} 204 */ 205 addUIImage:function (image, key) { 206 cc.Assert(image != null, "TextureCache: image MUST not be nulll"); 207 208 var texture = null; 209 210 if (key) { 211 if (this.textures.hasOwnProperty(key)) { 212 texture = this.textures[key]; 213 if (texture) { 214 return texture; 215 } 216 } 217 } 218 219 // prevents overloading the autorelease pool 220 texture = new cc.Texture2D(); 221 texture.initWithImage(image); 222 223 if ((key != null) && (texture != null)) { 224 this.textures[key] = texture; 225 } else { 226 cc.log("cocos2d: Couldn't add UIImage in TextureCache"); 227 } 228 229 return texture; 230 }, 231 232 /** 233 * Returns an already created texture. Returns null if the texture doesn't exist. 234 * @param {String} key 235 * @return {Image|Null} 236 * @example 237 * //example 238 * var key = cc.TextureCache.getInstance().textureForKey("hello.png"); 239 */ 240 textureForKey:function (key) { 241 if (this.textures.hasOwnProperty(key)) { 242 return this.textures[key]; 243 } else { 244 return null; 245 } 246 }, 247 248 /** 249 * @param {Image} texture 250 * @return {String|Null} 251 * @example 252 * //example 253 * var key = cc.TextureCache.getInstance().getKeyByTexture(texture); 254 */ 255 getKeyByTexture:function (texture) { 256 for (var key in this.textures) { 257 if (this.textures[key] == texture) { 258 return key; 259 } 260 } 261 return null; 262 }, 263 264 _generalTextureKey:function(){ 265 this._textureKeySeq++; 266 return "_textureKey_" + this._textureKeySeq; 267 }, 268 269 /** 270 * @param {Image} texture 271 * @return {Array} 272 * @example 273 * //example 274 * var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(texture); 275 */ 276 getTextureColors:function (texture) { 277 var key = this.getKeyByTexture(texture); 278 if (!key) { 279 if (texture instanceof HTMLImageElement) { 280 key = texture.src; 281 } else { 282 key = this._generalTextureKey(); 283 } 284 } 285 286 if (!this._textureColorsCache.hasOwnProperty(key)) { 287 this._textureColorsCache[key] = cc.generateTextureCacheForColor(texture); 288 } 289 return this._textureColorsCache[key]; 290 }, 291 292 /** 293 * <p>Purges the dictionary of loaded textures. <br /> 294 * Call this method if you receive the "Memory Warning" <br /> 295 * In the short term: it will free some resources preventing your app from being killed <br /> 296 * In the medium term: it will allocate more resources <br /> 297 * In the long term: it will be the same</p> 298 * @example 299 * //example 300 * cc.TextureCache.getInstance().removeAllTextures(); 301 */ 302 removeAllTextures:function () { 303 this.textures = {}; 304 }, 305 306 /** 307 * Deletes a texture from the cache given a texture 308 * @param {Image} texture 309 * @example 310 * //example 311 * cc.TextureCache.getInstance().removeTexture(texture); 312 */ 313 removeTexture:function (texture) { 314 if (!texture) 315 return; 316 317 for (var key in this.textures) { 318 if (this.textures[key] == texture) { 319 delete(this.textures[key]); 320 return; 321 } 322 } 323 }, 324 325 /** 326 * Deletes a texture from the cache given a its key name 327 * @param {String} textureKeyName 328 * @example 329 * //example 330 * cc.TextureCache.getInstance().removeTexture("hello.png"); 331 */ 332 removeTextureForKey:function (textureKeyName) { 333 if (textureKeyName == null) { 334 return; 335 } 336 if (this.textures[textureKeyName]) { 337 delete(this.textures[textureKeyName]); 338 } 339 }, 340 341 /** 342 * <p>Output to cc.log the current contents of this TextureCache <br /> 343 * This will attempt to calculate the size of each texture, and the total texture memory in use. </p> 344 */ 345 dumpCachedTextureInfo:function () { 346 var count = 0; 347 var totalBytes = 0; 348 for (var key in this.textures) { 349 var tex = this.textures[key]; 350 var bpp = tex.bitsPerPixelForFormat(); 351 // Each texture takes up width * height * bytesPerPixel bytes. 352 var bytes = tex.getPixelsWide() * tex.getPixelsHigh() * bpp / 8; 353 totalBytes += bytes; 354 count++; 355 cc.log("cocos2d: '" + tex.toString() + "' id=" + tex.getName() + " " + tex.getPixelsWide() + " x " + tex.getPixelsHigh() + " @ " + bpp + " bpp => " + bytes / 1024 + " KB"); 356 } 357 358 cc.log("cocos2d: TextureCache dumpDebugInfo: " + count + " textures, for " + (totalBytes / 1024) + " KB (" + (totalBytes / (1024.0 * 1024.0)).toFixed(2) + " MB)"); 359 }, 360 361 /** 362 * <p>Returns a Texture2D object given an PVR filename<br /> 363 * If the file image was not previously loaded, it will create a new Texture2D<br /> 364 * object and it will return it. Otherwise it will return a reference of a previously loaded image </p> 365 * @param {String} path 366 * @return {cc.Texture2D} 367 */ 368 addPVRImage:function (path) { 369 cc.Assert(path != null, "TextureCache: file image MUST not be null"); 370 371 path = cc.FileUtils.getInstance().fullPathFromRelativePath(path); 372 373 var key = path; 374 375 if (this.textures[key] != null) { 376 return this.textures[key]; 377 } 378 379 // Split up directory and filename 380 var tex = new cc.Texture2D(); 381 if (tex.initWithPVRFile(key)) { 382 this.textures[key] = tex; 383 } else { 384 cc.log("cocos2d: Couldn't add PVRImage:" + key + " in TextureCache"); 385 } 386 387 return tex; 388 } 389 }); 390 391 /** 392 * Return ths shared instance of the cache 393 * @return {cc.TextureCache} 394 */ 395 cc.TextureCache.getInstance = function () { 396 if (!cc.g_sharedTextureCache) 397 cc.g_sharedTextureCache = new cc.TextureCache(); 398 return cc.g_sharedTextureCache; 399 }; 400 401 /** 402 * Purges the cache. It releases the retained instance. 403 */ 404 cc.TextureCache.purgeSharedTextureCache = function () { 405 cc.g_sharedTextureCache = null; 406 }; 407