package org.anddev.andengine.opengl.texture; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import javax.microedition.khronos.opengles.GL10; import org.anddev.andengine.util.Debug; /** * (c) 2010 Nicolas Gramlich * (c) 2011 Zynga Inc. * * @author Nicolas Gramlich * @since 17:48:46 - 08.03.2010 */ public class TextureManager { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final HashSet<ITexture> mTexturesManaged = new HashSet<ITexture>(); private final ArrayList<ITexture> mTexturesLoaded = new ArrayList<ITexture>(); private final ArrayList<ITexture> mTexturesToBeLoaded = new ArrayList<ITexture>(); private final ArrayList<ITexture> mTexturesToBeUnloaded = new ArrayList<ITexture>(); // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== protected synchronized void clear() { this.mTexturesToBeLoaded.clear(); this.mTexturesLoaded.clear(); this.mTexturesManaged.clear(); } /** * @param pTexture the {@link ITexture} to be loaded before the very next frame is drawn (Or prevent it from being unloaded then). * @return <code>true</code> when the {@link ITexture} was previously not managed by this {@link TextureManager}, <code>false</code> if it was already managed. */ public synchronized boolean loadTexture(final ITexture pTexture) { if(this.mTexturesManaged.contains(pTexture)) { /* Just make sure it doesn't get deleted. */ this.mTexturesToBeUnloaded.remove(pTexture); return false; } else { this.mTexturesManaged.add(pTexture); this.mTexturesToBeLoaded.add(pTexture); return true; } } /** * @param pTexture the {@link ITexture} to be unloaded before the very next frame is drawn (Or prevent it from being loaded then). * @return <code>true</code> when the {@link ITexture} was already managed by this {@link TextureManager}, <code>false</code> if it was not managed. */ public synchronized boolean unloadTexture(final ITexture pTexture) { if(this.mTexturesManaged.contains(pTexture)) { /* If the Texture is loaded, unload it. * If the Texture is about to be loaded, stop it from being loaded. */ if(this.mTexturesLoaded.contains(pTexture)){ this.mTexturesToBeUnloaded.add(pTexture); } else if(this.mTexturesToBeLoaded.remove(pTexture)){ this.mTexturesManaged.remove(pTexture); } return true; } else { return false; } } public void loadTextures(final ITexture ... pTextures) { for(int i = pTextures.length - 1; i >= 0; i--) { this.loadTexture(pTextures[i]); } } public void unloadTextures(final ITexture ... pTextures) { for(int i = pTextures.length - 1; i >= 0; i--) { this.unloadTexture(pTextures[i]); } } public synchronized void reloadTextures() { final HashSet<ITexture> managedTextures = this.mTexturesManaged; for(final ITexture texture : managedTextures) { // TODO Can the use of the iterator be avoided somehow? texture.setLoadedToHardware(false); } this.mTexturesToBeLoaded.addAll(this.mTexturesLoaded); // TODO Check if addAll uses iterator internally! this.mTexturesLoaded.clear(); this.mTexturesManaged.removeAll(this.mTexturesToBeUnloaded); // TODO Check if removeAll uses iterator internally! this.mTexturesToBeUnloaded.clear(); } public synchronized void updateTextures(final GL10 pGL) { final HashSet<ITexture> texturesManaged = this.mTexturesManaged; final ArrayList<ITexture> texturesLoaded = this.mTexturesLoaded; final ArrayList<ITexture> texturesToBeLoaded = this.mTexturesToBeLoaded; final ArrayList<ITexture> texturesToBeUnloaded = this.mTexturesToBeUnloaded; /* First reload Textures that need to be updated. */ final int textursLoadedCount = texturesLoaded.size(); if(textursLoadedCount > 0){ for(int i = textursLoadedCount - 1; i >= 0; i--){ final ITexture textureToBeReloaded = texturesLoaded.get(i); if(textureToBeReloaded.isUpdateOnHardwareNeeded()){ try { textureToBeReloaded.reloadToHardware(pGL); } catch(IOException e) { Debug.e(e); } } } } /* Then load pending Textures. */ final int texturesToBeLoadedCount = texturesToBeLoaded.size(); if(texturesToBeLoadedCount > 0){ for(int i = texturesToBeLoadedCount - 1; i >= 0; i--){ final ITexture textureToBeLoaded = texturesToBeLoaded.remove(i); if(!textureToBeLoaded.isLoadedToHardware()){ try { textureToBeLoaded.loadToHardware(pGL); } catch(IOException e) { Debug.e(e); } } texturesLoaded.add(textureToBeLoaded); } } /* Then unload pending Textures. */ final int texturesToBeUnloadedCount = texturesToBeUnloaded.size(); if(texturesToBeUnloadedCount > 0){ for(int i = texturesToBeUnloadedCount - 1; i >= 0; i--){ final ITexture textureToBeUnloaded = texturesToBeUnloaded.remove(i); if(textureToBeUnloaded.isLoadedToHardware()){ textureToBeUnloaded.unloadFromHardware(pGL); } texturesLoaded.remove(textureToBeUnloaded); texturesManaged.remove(textureToBeUnloaded); } } /* Finally invoke the GC if anything has changed. */ if(texturesToBeLoadedCount > 0 || texturesToBeUnloadedCount > 0){ System.gc(); } } // =========================================================== // Inner and Anonymous Classes // =========================================================== }