• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.graphics;
18 
19 import java.util.HashMap;
20 import java.util.Map;
21 
22 import com.badlogic.gdx.Application;
23 import com.badlogic.gdx.Gdx;
24 import com.badlogic.gdx.assets.AssetLoaderParameters.LoadedCallback;
25 import com.badlogic.gdx.assets.AssetManager;
26 import com.badlogic.gdx.assets.loaders.AssetLoader;
27 import com.badlogic.gdx.assets.loaders.TextureLoader.TextureParameter;
28 import com.badlogic.gdx.files.FileHandle;
29 import com.badlogic.gdx.graphics.Pixmap.Format;
30 import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
31 import com.badlogic.gdx.utils.Array;
32 import com.badlogic.gdx.utils.GdxRuntimeException;
33 
34 /** A Texture wraps a standard OpenGL ES texture.
35  * <p>
36  * A Texture can be managed. If the OpenGL context is lost all managed textures get invalidated. This happens when a user switches
37  * to another application or receives an incoming call. Managed textures get reloaded automatically.
38  * <p>
39  * A Texture has to be bound via the {@link Texture#bind()} method in order for it to be applied to geometry. The texture will be
40  * bound to the currently active texture unit specified via {@link GL20#glActiveTexture(int)}.
41  * <p>
42  * You can draw {@link Pixmap}s to a texture at any time. The changes will be automatically uploaded to texture memory. This is of
43  * course not extremely fast so use it with care. It also only works with unmanaged textures.
44  * <p>
45  * A Texture must be disposed when it is no longer used
46  * @author badlogicgames@gmail.com */
47 public class Texture extends GLTexture {
48 	private static AssetManager assetManager;
49 	final static Map<Application, Array<Texture>> managedTextures = new HashMap<Application, Array<Texture>>();
50 
51 	public enum TextureFilter {
52 		Nearest(GL20.GL_NEAREST), Linear(GL20.GL_LINEAR), MipMap(GL20.GL_LINEAR_MIPMAP_LINEAR), MipMapNearestNearest(
53 			GL20.GL_NEAREST_MIPMAP_NEAREST), MipMapLinearNearest(GL20.GL_LINEAR_MIPMAP_NEAREST), MipMapNearestLinear(
54 			GL20.GL_NEAREST_MIPMAP_LINEAR), MipMapLinearLinear(GL20.GL_LINEAR_MIPMAP_LINEAR);
55 
56 		final int glEnum;
57 
TextureFilter(int glEnum)58 		TextureFilter (int glEnum) {
59 			this.glEnum = glEnum;
60 		}
61 
isMipMap()62 		public boolean isMipMap () {
63 			return glEnum != GL20.GL_NEAREST && glEnum != GL20.GL_LINEAR;
64 		}
65 
getGLEnum()66 		public int getGLEnum () {
67 			return glEnum;
68 		}
69 	}
70 
71 	public enum TextureWrap {
72 		MirroredRepeat(GL20.GL_MIRRORED_REPEAT), ClampToEdge(GL20.GL_CLAMP_TO_EDGE), Repeat(GL20.GL_REPEAT);
73 
74 		final int glEnum;
75 
TextureWrap(int glEnum)76 		TextureWrap (int glEnum) {
77 			this.glEnum = glEnum;
78 		}
79 
getGLEnum()80 		public int getGLEnum () {
81 			return glEnum;
82 		}
83 	}
84 
85 	TextureData data;
86 
Texture(String internalPath)87 	public Texture (String internalPath) {
88 		this(Gdx.files.internal(internalPath));
89 	}
90 
Texture(FileHandle file)91 	public Texture (FileHandle file) {
92 		this(file, null, false);
93 	}
94 
Texture(FileHandle file, boolean useMipMaps)95 	public Texture (FileHandle file, boolean useMipMaps) {
96 		this(file, null, useMipMaps);
97 	}
98 
Texture(FileHandle file, Format format, boolean useMipMaps)99 	public Texture (FileHandle file, Format format, boolean useMipMaps) {
100 		this(TextureData.Factory.loadFromFile(file, format, useMipMaps));
101 	}
102 
Texture(Pixmap pixmap)103 	public Texture (Pixmap pixmap) {
104 		this(new PixmapTextureData(pixmap, null, false, false));
105 	}
106 
Texture(Pixmap pixmap, boolean useMipMaps)107 	public Texture (Pixmap pixmap, boolean useMipMaps) {
108 		this(new PixmapTextureData(pixmap, null, useMipMaps, false));
109 	}
110 
Texture(Pixmap pixmap, Format format, boolean useMipMaps)111 	public Texture (Pixmap pixmap, Format format, boolean useMipMaps) {
112 		this(new PixmapTextureData(pixmap, format, useMipMaps, false));
113 	}
114 
Texture(int width, int height, Format format)115 	public Texture (int width, int height, Format format) {
116 		this(new PixmapTextureData(new Pixmap(width, height, format), null, false, true));
117 	}
118 
Texture(TextureData data)119 	public Texture (TextureData data) {
120 		this(GL20.GL_TEXTURE_2D, Gdx.gl.glGenTexture(), data);
121 	}
122 
Texture(int glTarget, int glHandle, TextureData data)123 	protected Texture (int glTarget, int glHandle, TextureData data) {
124 		super(glTarget, glHandle);
125 		load(data);
126 		if (data.isManaged()) addManagedTexture(Gdx.app, this);
127 	}
128 
load(TextureData data)129 	public void load (TextureData data) {
130 		if (this.data != null && data.isManaged() != this.data.isManaged())
131 			throw new GdxRuntimeException("New data must have the same managed status as the old data");
132 		this.data = data;
133 
134 		if (!data.isPrepared()) data.prepare();
135 
136 		bind();
137 		uploadImageData(GL20.GL_TEXTURE_2D, data);
138 
139 		setFilter(minFilter, magFilter);
140 		setWrap(uWrap, vWrap);
141 		Gdx.gl.glBindTexture(glTarget, 0);
142 	}
143 
144 	/** Used internally to reload after context loss. Creates a new GL handle then calls {@link #load(TextureData)}. Use this only
145 	 * if you know what you do! */
146 	@Override
reload()147 	protected void reload () {
148 		if (!isManaged()) throw new GdxRuntimeException("Tried to reload unmanaged Texture");
149 		glHandle = Gdx.gl.glGenTexture();
150 		load(data);
151 	}
152 
153 	/** Draws the given {@link Pixmap} to the texture at position x, y. No clipping is performed so you have to make sure that you
154 	 * draw only inside the texture region. Note that this will only draw to mipmap level 0!
155 	 *
156 	 * @param pixmap The Pixmap
157 	 * @param x The x coordinate in pixels
158 	 * @param y The y coordinate in pixels */
draw(Pixmap pixmap, int x, int y)159 	public void draw (Pixmap pixmap, int x, int y) {
160 		if (data.isManaged()) throw new GdxRuntimeException("can't draw to a managed texture");
161 
162 		bind();
163 		Gdx.gl.glTexSubImage2D(glTarget, 0, x, y, pixmap.getWidth(), pixmap.getHeight(), pixmap.getGLFormat(), pixmap.getGLType(),
164 			pixmap.getPixels());
165 	}
166 
167 	@Override
getWidth()168 	public int getWidth () {
169 		return data.getWidth();
170 	}
171 
172 	@Override
getHeight()173 	public int getHeight () {
174 		return data.getHeight();
175 	}
176 
177 	@Override
getDepth()178 	public int getDepth () {
179 		return 0;
180 	}
181 
getTextureData()182 	public TextureData getTextureData () {
183 		return data;
184 	}
185 
186 	/** @return whether this texture is managed or not. */
isManaged()187 	public boolean isManaged () {
188 		return data.isManaged();
189 	}
190 
191 	/** Disposes all resources associated with the texture */
dispose()192 	public void dispose () {
193 		// this is a hack. reason: we have to set the glHandle to 0 for textures that are
194 		// reloaded through the asset manager as we first remove (and thus dispose) the texture
195 		// and then reload it. the glHandle is set to 0 in invalidateAllTextures prior to
196 		// removal from the asset manager.
197 		if (glHandle == 0) return;
198 		delete();
199 		if (data.isManaged()) if (managedTextures.get(Gdx.app) != null) managedTextures.get(Gdx.app).removeValue(this, true);
200 	}
201 
addManagedTexture(Application app, Texture texture)202 	private static void addManagedTexture (Application app, Texture texture) {
203 		Array<Texture> managedTextureArray = managedTextures.get(app);
204 		if (managedTextureArray == null) managedTextureArray = new Array<Texture>();
205 		managedTextureArray.add(texture);
206 		managedTextures.put(app, managedTextureArray);
207 	}
208 
209 	/** Clears all managed textures. This is an internal method. Do not use it! */
clearAllTextures(Application app)210 	public static void clearAllTextures (Application app) {
211 		managedTextures.remove(app);
212 	}
213 
214 	/** Invalidate all managed textures. This is an internal method. Do not use it! */
invalidateAllTextures(Application app)215 	public static void invalidateAllTextures (Application app) {
216 		Array<Texture> managedTextureArray = managedTextures.get(app);
217 		if (managedTextureArray == null) return;
218 
219 		if (assetManager == null) {
220 			for (int i = 0; i < managedTextureArray.size; i++) {
221 				Texture texture = managedTextureArray.get(i);
222 				texture.reload();
223 			}
224 		} else {
225 			// first we have to make sure the AssetManager isn't loading anything anymore,
226 			// otherwise the ref counting trick below wouldn't work (when a texture is
227 			// currently on the task stack of the manager.)
228 			assetManager.finishLoading();
229 
230 			// next we go through each texture and reload either directly or via the
231 			// asset manager.
232 			Array<Texture> textures = new Array<Texture>(managedTextureArray);
233 			for (Texture texture : textures) {
234 				String fileName = assetManager.getAssetFileName(texture);
235 				if (fileName == null) {
236 					texture.reload();
237 				} else {
238 					// get the ref count of the texture, then set it to 0 so we
239 					// can actually remove it from the assetmanager. Also set the
240 					// handle to zero, otherwise we might accidentially dispose
241 					// already reloaded textures.
242 					final int refCount = assetManager.getReferenceCount(fileName);
243 					assetManager.setReferenceCount(fileName, 0);
244 					texture.glHandle = 0;
245 
246 					// create the parameters, passing the reference to the texture as
247 					// well as a callback that sets the ref count.
248 					TextureParameter params = new TextureParameter();
249 					params.textureData = texture.getTextureData();
250 					params.minFilter = texture.getMinFilter();
251 					params.magFilter = texture.getMagFilter();
252 					params.wrapU = texture.getUWrap();
253 					params.wrapV = texture.getVWrap();
254 					params.genMipMaps = texture.data.useMipMaps(); // not sure about this?
255 					params.texture = texture; // special parameter which will ensure that the references stay the same.
256 					params.loadedCallback = new LoadedCallback() {
257 						@Override
258 						public void finishedLoading (AssetManager assetManager, String fileName, Class type) {
259 							assetManager.setReferenceCount(fileName, refCount);
260 						}
261 					};
262 
263 					// unload the texture, create a new gl handle then reload it.
264 					assetManager.unload(fileName);
265 					texture.glHandle = Gdx.gl.glGenTexture();
266 					assetManager.load(fileName, Texture.class, params);
267 				}
268 			}
269 			managedTextureArray.clear();
270 			managedTextureArray.addAll(textures);
271 		}
272 	}
273 
274 	/** Sets the {@link AssetManager}. When the context is lost, textures managed by the asset manager are reloaded by the manager
275 	 * on a separate thread (provided that a suitable {@link AssetLoader} is registered with the manager). Textures not managed by
276 	 * the AssetManager are reloaded via the usual means on the rendering thread.
277 	 * @param manager the asset manager. */
setAssetManager(AssetManager manager)278 	public static void setAssetManager (AssetManager manager) {
279 		Texture.assetManager = manager;
280 	}
281 
getManagedStatus()282 	public static String getManagedStatus () {
283 		StringBuilder builder = new StringBuilder();
284 		builder.append("Managed textures/app: { ");
285 		for (Application app : managedTextures.keySet()) {
286 			builder.append(managedTextures.get(app).size);
287 			builder.append(" ");
288 		}
289 		builder.append("}");
290 		return builder.toString();
291 	}
292 
293 	/** @return the number of managed textures currently loaded */
getNumManagedTextures()294 	public static int getNumManagedTextures () {
295 		return managedTextures.get(Gdx.app).size;
296 	}
297 }
298