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 The global cache for cc.Texture2D
 29  */
 30 cc.textureCache = /** @lends cc.textureCache# */{
 31     _textures: {},
 32     _textureColorsCache: {},
 33     _textureKeySeq:(0 | Math.random() * 1000),
 34 
 35     _loadedTexturesBefore: {},
 36 
 37     handleLoadedTexture : function(url){
 38         var locTexs = this._textures;
 39         if(cc._renderType === cc._RENDER_TYPE_WEBGL && !cc._rendererInitialized){
 40             locTexs = this._loadedTexturesBefore;
 41         }
 42         var tex = locTexs[url];
 43         if(!tex) {
 44             tex = locTexs[url] = new cc.Texture2D();
 45             tex.url = url;
 46         }
 47         tex.handleLoadedTexture();
 48     },
 49 
 50     _initializingRenderer : function(){
 51         var selPath;
 52         //init texture from _loadedTexturesBefore
 53         var locLoadedTexturesBefore = this._loadedTexturesBefore, locTextures = this._textures;
 54         for(selPath in locLoadedTexturesBefore){
 55             var tex2d = locLoadedTexturesBefore[selPath];
 56             tex2d.handleLoadedTexture();
 57             locTextures[selPath] = tex2d;
 58         }
 59         this._loadedTexturesBefore = {};
 60     },
 61 
 62     /**
 63      * <p>
 64      *     Returns a Texture2D object given an PVR filename                                                              <br/>
 65      *     If the file image was not previously loaded, it will create a new CCTexture2D                                 <br/>
 66      *     object and it will return it. Otherwise it will return a reference of a previously loaded image              <br/>
 67      *     note: AddPVRTCImage does not support on HTML5
 68      * </p>
 69      * @param {String} filename
 70      * @return {cc.Texture2D}
 71      */
 72     addPVRTCImage:function (filename) {
 73         cc.log("TextureCache:addPVRTCImage does not support on HTML5");
 74     },
 75 
 76 
 77     /**
 78      * <p>
 79      *     Returns a Texture2D object given an ETC filename                                                               <br/>
 80      *     If the file image was not previously loaded, it will create a new CCTexture2D                                  <br/>
 81      *     object and it will return it. Otherwise it will return a reference of a previously loaded image                <br/>
 82      *    note:addETCImage does not support on HTML5
 83      * </p>
 84      * @param {String} filename
 85      * @return {cc.Texture2D}
 86      */
 87     addETCImage:function (filename) {
 88         cc.log("TextureCache:addPVRTCImage does not support on HTML5");
 89     },
 90 
 91     /**
 92      * Description
 93      * @return {String}
 94      */
 95     description:function () {
 96         return "<TextureCache | Number of textures = " + this._textures.length + ">";
 97     },
 98 
 99     /**
100      * Returns an already created texture. Returns null if the texture doesn't exist.
101      * @param {String} textureKeyName
102      * @return {cc.Texture2D|Null}
103      * @example
104      * //example
105      * var key = cc.textureCache.textureForKey("hello.png");
106      */
107     textureForKey:function (textureKeyName) {
108         return this._textures[textureKeyName] || this._textures[cc.loader._aliases[textureKeyName]];
109     },
110 
111     /**
112      * @param {Image} texture
113      * @return {String|Null}
114      * @example
115      * //example
116      * var key = cc.textureCache.getKeyByTexture(texture);
117      */
118     getKeyByTexture:function (texture) {
119         for (var key in this._textures) {
120             if (this._textures[key] == texture) {
121                 return key;
122             }
123         }
124         return null;
125     },
126 
127     _generalTextureKey:function () {
128         this._textureKeySeq++;
129         return "_textureKey_" + this._textureKeySeq;
130     },
131 
132     /**
133      * @param {Image} texture
134      * @return {Array}
135      * @example
136      * //example
137      * var cacheTextureForColor = cc.textureCache.getTextureColors(texture);
138      */
139     getTextureColors:function (texture) {
140         var key = this.getKeyByTexture(texture);
141         if (!key) {
142             if (texture instanceof HTMLImageElement)
143                 key = texture.src;
144             else
145                 key = this._generalTextureKey();
146         }
147 
148         if (!this._textureColorsCache[key])
149             this._textureColorsCache[key] = cc.generateTextureCacheForColor(texture);
150         return this._textureColorsCache[key];
151     },
152 
153     /**
154      * <p>Returns a Texture2D object given an PVR filename<br />
155      * If the file image was not previously loaded, it will create a new Texture2D<br />
156      *  object and it will return it. Otherwise it will return a reference of a previously loaded image </p>
157      * @param {String} path
158      * @return {cc.Texture2D}
159      */
160     addPVRImage:function (path) {
161         cc.log("addPVRImage does not support on HTML5");
162     },
163 
164     /**
165      * <p>Purges the dictionary of loaded textures. <br />
166      * Call this method if you receive the "Memory Warning"  <br />
167      * In the short term: it will free some resources preventing your app from being killed  <br />
168      * In the medium term: it will allocate more resources <br />
169      * In the long term: it will be the same</p>
170      * @example
171      * //example
172      * cc.textureCache.removeAllTextures();
173      */
174     removeAllTextures:function () {
175         var locTextures = this._textures;
176         for (var selKey in locTextures) {
177             if(locTextures[selKey])
178                 locTextures[selKey].releaseTexture();
179         }
180         this._textures = {};
181     },
182 
183     /**
184      * Deletes a texture from the cache given a texture
185      * @param {Image} texture
186      * @example
187      * //example
188      * cc.textureCache.removeTexture(texture);
189      */
190     removeTexture:function (texture) {
191         if (!texture)
192             return;
193 
194         var locTextures = this._textures;
195         for (var selKey in locTextures) {
196             if (locTextures[selKey] == texture) {
197                 locTextures[selKey].releaseTexture();
198                 delete(locTextures[selKey]);
199             }
200         }
201     },
202 
203     /**
204      * Deletes a texture from the cache given a its key name
205      * @param {String} textureKeyName
206      * @example
207      * //example
208      * cc.textureCache.removeTexture("hello.png");
209      */
210     removeTextureForKey:function (textureKeyName) {
211         if (textureKeyName == null)
212             return;
213         if (this._textures[textureKeyName])
214             delete(this._textures[textureKeyName]);
215     },
216 
217     /**
218      * <p>Returns a Texture2D object given an file image <br />
219      * If the file image was not previously loaded, it will create a new Texture2D <br />
220      *  object and it will return it. It will use the filename as a key.<br />
221      * Otherwise it will return a reference of a previously loaded image. <br />
222      * Supported image extensions: .png, .jpg, .gif</p>
223      * @param {String} url
224      * @return {cc.Texture2D}
225      * @example
226      * //example
227      * cc.textureCache.addImage("hello.png");
228      */
229     addImage:function (url, target, cb) {
230         if(!url)
231             throw "cc.Texture.addImage(): path should be non-null";
232         if(arguments.length == 2){
233             cb = target;
234             target = null;
235         }
236         var locTexs = this._textures;
237         if(cc._renderType === cc._RENDER_TYPE_WEBGL && !cc._rendererInitialized){
238             locTexs = this._loadedTexturesBefore;
239         }
240         var tex = locTexs[url] || locTexs[cc.loader._aliases[url]];
241         if(tex) {
242             if(cb) cb.call(target);
243             return tex;
244         }
245 
246         if(!cc.loader.getRes(url)){
247             cc.loader.load(url, function(err){
248                 if(cb) cb.call(target);
249             });
250         }
251 
252         tex = locTexs[url] = new cc.Texture2D();
253         tex.url = url;
254         return tex;
255     },
256 
257     /**
258      *  Cache the image data
259      * @param {String} path
260      * @param {Image|HTMLImageElement|HTMLCanvasElement} texture
261      */
262     cacheImage:function (path, texture) {
263         if(texture instanceof  cc.Texture2D){
264             this._textures[path] = texture;
265             return ;
266         }
267         var texture2d = new cc.Texture2D();
268         texture2d.initWithElement(texture);
269         texture2d.handleLoadedTexture();
270         this._textures[path] = texture2d;
271     },
272 
273     /**
274      * <p>Returns a Texture2D object given an UIImage image<br />
275      * If the image was not previously loaded, it will create a new Texture2D object and it will return it.<br />
276      * Otherwise it will return a reference of a previously loaded image<br />
277      * The "key" parameter will be used as the "key" for the cache.<br />
278      * If "key" is null, then a new texture will be created each time.</p>
279      * @param {HTMLImageElement|HTMLCanvasElement} image
280      * @param {String} key
281      * @return {cc.Texture2D}
282      */
283     addUIImage:function (image, key) {
284         if(!image)
285             throw "cc.Texture.addUIImage(): image should be non-null";
286 
287         if (key) {
288             if (this._textures[key])
289                 return this._textures[key];
290         }
291 
292         // prevents overloading the autorelease pool
293         var texture = new cc.Texture2D();
294         texture.initWithImage(image);
295         if ((key != null) && (texture != null))
296             this._textures[key] = texture;
297         else
298             cc.log("cocos2d: Couldn't add UIImage in TextureCache");
299         return texture;
300     },
301 
302     /**
303      * <p>Output to cc.log the current contents of this TextureCache <br />
304      * This will attempt to calculate the size of each texture, and the total texture memory in use. </p>
305      */
306     dumpCachedTextureInfo:function () {
307         var count = 0;
308         var totalBytes = 0, locTextures = this._textures;
309 
310         for (var key in locTextures) {
311             var selTexture = locTextures[key];
312             count++;
313             if (selTexture.getHtmlElementObj() instanceof  HTMLImageElement)
314                 cc.log("cocos2d: '" + key + "' id=" + selTexture.getHtmlElementObj().src + " " + selTexture.pixelsWidth + " x " + selTexture.pixelsHeight);
315             else {
316                 cc.log("cocos2d: '" + key + "' id= HTMLCanvasElement " + selTexture.pixelsWidth + " x " + selTexture.pixelsHeight);
317             }
318             totalBytes += selTexture.pixelsWidth * selTexture.pixelsHeight * 4;
319         }
320 
321         var locTextureColorsCache = this._textureColorsCache;
322         for (key in locTextureColorsCache) {
323             var selCanvasColorsArr = locTextureColorsCache[key];
324             for (var selCanvasKey in selCanvasColorsArr){
325                 var selCanvas = selCanvasColorsArr[selCanvasKey];
326                 count++;
327                 cc.log("cocos2d: '" + key + "' id= HTMLCanvasElement " + selCanvas.width + " x " + selCanvas.height);
328                 totalBytes += selCanvas.width * selCanvas.height * 4;
329             }
330 
331         }
332         cc.log("cocos2d: TextureCache dumpDebugInfo: " + count + " textures, HTMLCanvasElement for "
333             + (totalBytes / 1024) + " KB (" + (totalBytes / (1024.0 * 1024.0)).toFixed(2) + " MB)");
334     },
335 
336 	_clear: function () {
337 		this._textures = {};
338 		this._textureColorsCache = {};
339 		this._textureKeySeq = (0 | Math.random() * 1000);
340 		this._loadedTexturesBefore = {};
341 	}
342 };