Main repository of MikuMikuStudio
Revision | 8cf668e1e18989c6fe4077e4a7676c3964477937 (tree) |
---|---|
Time | 2013-03-16 05:45:36 |
Author | remy.bouquet@gmail.com <remy.bouquet@gmai...> |
Commiter | remy.bouquet@gmail.com |
Android texture util now supports uploading a sub texture to the GPU, even as a bitmap.
This makes Nifty batch rendering work on android.
git-svn-id: http://jmonkeyengine.googlecode.com/svn/trunk@10488 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
@@ -18,14 +18,13 @@ import java.util.logging.Logger; | ||
18 | 18 | public class TextureUtil { |
19 | 19 | |
20 | 20 | private static final Logger logger = Logger.getLogger(TextureUtil.class.getName()); |
21 | - | |
22 | 21 | //TODO Make this configurable through appSettings |
23 | 22 | public static boolean ENABLE_COMPRESSION = true; |
24 | 23 | private static boolean NPOT = false; |
25 | 24 | private static boolean ETC1support = false; |
26 | 25 | private static boolean DXT1 = false; |
27 | 26 | private static boolean DEPTH24 = false; |
28 | - | |
27 | + | |
29 | 28 | public static void loadTextureFeatures(String extensionString) { |
30 | 29 | ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture"); |
31 | 30 | DEPTH24 = extensionString.contains("GL_OES_depth24"); |
@@ -36,21 +35,21 @@ public class TextureUtil { | ||
36 | 35 | logger.log(Level.FINE, "Supports NPOT? {0}", NPOT); |
37 | 36 | logger.log(Level.FINE, "Supports DXT1? {0}", DXT1); |
38 | 37 | } |
39 | - | |
38 | + | |
40 | 39 | private static void buildMipmap(Bitmap bitmap, boolean compress) { |
41 | 40 | int level = 0; |
42 | 41 | int height = bitmap.getHeight(); |
43 | 42 | int width = bitmap.getWidth(); |
44 | - | |
43 | + | |
45 | 44 | logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE"); |
46 | 45 | |
47 | 46 | GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); |
48 | - | |
47 | + | |
49 | 48 | while (height >= 1 || width >= 1) { |
50 | 49 | //First of all, generate the texture from our bitmap and set it to the according level |
51 | 50 | if (compress) { |
52 | 51 | logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height}); |
53 | - uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap); | |
52 | + uploadBitmapAsCompressed(GLES20.GL_TEXTURE_2D, level, bitmap, false, 0, 0); | |
54 | 53 | } else { |
55 | 54 | logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height}); |
56 | 55 | GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, level, bitmap, 0); |
@@ -67,20 +66,26 @@ public class TextureUtil { | ||
67 | 66 | |
68 | 67 | // Recycle any bitmaps created as a result of scaling the bitmap. |
69 | 68 | // Do not recycle the original image (mipmap level 0) |
70 | - if (level != 0){ | |
69 | + if (level != 0) { | |
71 | 70 | bitmap.recycle(); |
72 | 71 | } |
73 | - | |
72 | + | |
74 | 73 | bitmap = bitmap2; |
75 | - | |
74 | + | |
76 | 75 | level++; |
77 | 76 | } |
78 | 77 | } |
79 | 78 | |
80 | - private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap) { | |
79 | + private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) { | |
81 | 80 | if (bitmap.hasAlpha()) { |
82 | 81 | logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present."); |
83 | - GLUtils.texImage2D(target, level, bitmap, 0); | |
82 | + if (subTexture) { | |
83 | + GLUtils.texSubImage2D(target, level, x, y, bitmap); | |
84 | + checkGLError(); | |
85 | + } else { | |
86 | + GLUtils.texImage2D(target, level, bitmap, 0); | |
87 | + checkGLError(); | |
88 | + } | |
84 | 89 | } else { |
85 | 90 | // Convert to RGB565 |
86 | 91 | int bytesPerPixel = 2; |
@@ -97,15 +102,15 @@ public class TextureUtil { | ||
97 | 102 | // Encode the image into the output bytebuffer |
98 | 103 | int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight()); |
99 | 104 | ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize); |
100 | - ETC1.encodeImage(inputImage, bitmap.getWidth(), | |
101 | - bitmap.getHeight(), | |
102 | - bytesPerPixel, | |
103 | - bytesPerPixel * bitmap.getWidth(), | |
104 | - compressedImage); | |
105 | - | |
105 | + ETC1.encodeImage(inputImage, bitmap.getWidth(), | |
106 | + bitmap.getHeight(), | |
107 | + bytesPerPixel, | |
108 | + bytesPerPixel * bitmap.getWidth(), | |
109 | + compressedImage); | |
110 | + | |
106 | 111 | // Delete the input image buffer |
107 | 112 | BufferUtils.destroyDirectBuffer(inputImage); |
108 | - | |
113 | + | |
109 | 114 | // Create an ETC1Texture from the compressed image data |
110 | 115 | ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage); |
111 | 116 |
@@ -113,55 +118,53 @@ public class TextureUtil { | ||
113 | 118 | if (bytesPerPixel == 2) { |
114 | 119 | int oldSize = (bitmap.getRowBytes() * bitmap.getHeight()); |
115 | 120 | int newSize = compressedImage.capacity(); |
116 | - logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float)oldSize/newSize}); | |
117 | - GLES20.glCompressedTexImage2D(target, | |
118 | - level, | |
119 | - ETC1.ETC1_RGB8_OES, | |
120 | - bitmap.getWidth(), | |
121 | - bitmap.getHeight(), | |
122 | - 0, | |
123 | - etc1tex.getData().capacity(), | |
124 | - etc1tex.getData()); | |
125 | - | |
121 | + logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize}); | |
122 | + if (subTexture) { | |
123 | + GLES20.glCompressedTexSubImage2D(target, | |
124 | + level, | |
125 | + x, y, | |
126 | + bitmap.getWidth(), | |
127 | + bitmap.getHeight(), | |
128 | + ETC1.ETC1_RGB8_OES, | |
129 | + etc1tex.getData().capacity(), | |
130 | + etc1tex.getData()); | |
131 | + checkGLError(); | |
132 | + } else { | |
133 | + GLES20.glCompressedTexImage2D(target, | |
134 | + level, | |
135 | + ETC1.ETC1_RGB8_OES, | |
136 | + bitmap.getWidth(), | |
137 | + bitmap.getHeight(), | |
138 | + 0, | |
139 | + etc1tex.getData().capacity(), | |
140 | + etc1tex.getData()); | |
141 | + checkGLError(); | |
142 | + } | |
143 | + | |
126 | 144 | // ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB, |
127 | 145 | // GLES20.GL_UNSIGNED_SHORT_5_6_5, etc1Texture); |
128 | 146 | // } else if (bytesPerPixel == 3) { |
129 | 147 | // ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB, |
130 | 148 | // GLES20.GL_UNSIGNED_BYTE, etc1Texture); |
131 | 149 | } |
132 | - | |
150 | + | |
133 | 151 | BufferUtils.destroyDirectBuffer(compressedImage); |
134 | 152 | } |
135 | 153 | } |
136 | - | |
154 | + | |
137 | 155 | /** |
138 | 156 | * <code>uploadTextureBitmap</code> uploads a native android bitmap |
139 | 157 | */ |
140 | 158 | public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) { |
159 | + uploadTextureBitmap(target, bitmap, needMips, false, 0, 0); | |
160 | + } | |
161 | + | |
162 | + /** | |
163 | + * <code>uploadTextureBitmap</code> uploads a native android bitmap | |
164 | + */ | |
165 | + public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) { | |
141 | 166 | boolean recycleBitmap = false; |
142 | - if (!NPOT || needMips) { | |
143 | - // Power of 2 images are not supported by this GPU. | |
144 | - // OR | |
145 | - // Mipmaps were requested to be used. | |
146 | - // Currently OGLES does not support NPOT textures with mipmaps. | |
147 | - int width = bitmap.getWidth(); | |
148 | - int height = bitmap.getHeight(); | |
149 | - | |
150 | - // If the image is not power of 2, rescale it | |
151 | - if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { | |
152 | - // Scale to power of two. | |
153 | - width = FastMath.nearestPowerOfTwo(width); | |
154 | - height = FastMath.nearestPowerOfTwo(height); | |
155 | - | |
156 | - logger.log(Level.WARNING, " - Image is not POT, so scaling it to new resolution: {0}x{1}", new Object[]{width, height}); | |
157 | - Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); | |
158 | - bitmap = bitmap2; | |
159 | - | |
160 | - // Flag to indicate that bitmap | |
161 | - // should be recycled at the end. | |
162 | - recycleBitmap = true; | |
163 | - } | |
164 | - } | |
167 | + //TODO, maybe this should raise an exception when NPOT is not supported | |
165 | 168 | |
166 | 169 | boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha(); |
167 | 170 | if (needMips && willCompress) { |
@@ -172,29 +175,39 @@ public class TextureUtil { | ||
172 | 175 | if (willCompress) { |
173 | 176 | // Image is compressed but mipmaps are not desired, upload directly. |
174 | 177 | logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated."); |
175 | - uploadBitmapAsCompressed(target, 0, bitmap); | |
178 | + uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y); | |
179 | + | |
176 | 180 | } else { |
177 | 181 | // Image is not compressed, mipmaps may or may not be desired. |
178 | - logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", | |
179 | - (needMips ? | |
180 | - " Mipmaps will be generated in HARDWARE" : | |
181 | - " Mipmaps are not generated.")); | |
182 | - GLUtils.texImage2D(target, 0, bitmap, 0); | |
182 | + logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", | |
183 | + (needMips | |
184 | + ? " Mipmaps will be generated in HARDWARE" | |
185 | + : " Mipmaps are not generated.")); | |
186 | + if (subTexture) { | |
187 | + System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight()); | |
188 | + GLUtils.texSubImage2D(target, 0, x, y, bitmap); | |
189 | + checkGLError(); | |
190 | + } else { | |
191 | + GLUtils.texImage2D(target, 0, bitmap, 0); | |
192 | + checkGLError(); | |
193 | + } | |
194 | + | |
183 | 195 | if (needMips) { |
184 | 196 | // No pregenerated mips available, |
185 | 197 | // generate from base level if required |
186 | 198 | GLES20.glGenerateMipmap(target); |
199 | + checkGLError(); | |
187 | 200 | } |
188 | 201 | } |
189 | 202 | } |
190 | - | |
203 | + | |
191 | 204 | if (recycleBitmap) { |
192 | 205 | bitmap.recycle(); |
193 | 206 | } |
194 | 207 | } |
195 | - | |
208 | + | |
196 | 209 | public static void uploadTextureAny(Image img, int target, int index, boolean needMips) { |
197 | - if (img.getEfficentData() instanceof AndroidImageInfo){ | |
210 | + if (img.getEfficentData() instanceof AndroidImageInfo) { | |
198 | 211 | logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img); |
199 | 212 | // If image was loaded from asset manager, use fast path |
200 | 213 | AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); |
@@ -206,14 +219,14 @@ public class TextureUtil { | ||
206 | 219 | logger.log(Level.WARNING, "Generating mipmaps is only" |
207 | 220 | + " supported for Bitmap based or non-compressed images!"); |
208 | 221 | } |
209 | - | |
222 | + | |
210 | 223 | // Upload using slower path |
211 | - logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", | |
212 | - (wantGeneratedMips ? | |
213 | - " Mipmaps will be generated in HARDWARE" : | |
214 | - " Mipmaps are not generated.")); | |
224 | + logger.log(Level.FINEST, " - Uploading bitmap directly.{0}", | |
225 | + (wantGeneratedMips | |
226 | + ? " Mipmaps will be generated in HARDWARE" | |
227 | + : " Mipmaps are not generated.")); | |
215 | 228 | uploadTexture(img, target, index); |
216 | - | |
229 | + | |
217 | 230 | // Image was uploaded using slower path, since it is not compressed, |
218 | 231 | // then compress it |
219 | 232 | if (wantGeneratedMips) { |
@@ -223,48 +236,14 @@ public class TextureUtil { | ||
223 | 236 | } |
224 | 237 | } |
225 | 238 | } |
226 | - | |
239 | + | |
227 | 240 | private static void unsupportedFormat(Format fmt) { |
228 | 241 | throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware."); |
229 | 242 | } |
230 | 243 | |
231 | - private static void uploadTexture(Image img, | |
232 | - int target, | |
233 | - int index){ | |
234 | - | |
235 | - if (img.getEfficentData() instanceof AndroidImageInfo){ | |
236 | - throw new RendererException("This image uses efficient data. " | |
237 | - + "Use uploadTextureBitmap instead."); | |
238 | - } | |
239 | - | |
240 | - // Otherwise upload image directly. | |
241 | - // Prefer to only use power of 2 textures here to avoid errors. | |
242 | - Image.Format fmt = img.getFormat(); | |
243 | - ByteBuffer data; | |
244 | - if (index >= 0 || img.getData() != null && img.getData().size() > 0){ | |
245 | - data = img.getData(index); | |
246 | - }else{ | |
247 | - data = null; | |
248 | - } | |
249 | - | |
250 | - int width = img.getWidth(); | |
251 | - int height = img.getHeight(); | |
252 | - int depth = img.getDepth(); | |
253 | - | |
254 | - if (!NPOT) { | |
255 | - // Check if texture is POT | |
256 | - if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { | |
257 | - throw new RendererException("Non-power-of-2 textures " | |
258 | - + "are not supported by the video hardware " | |
259 | - + "and no scaling path available for image: " + img); | |
260 | - } | |
261 | - } | |
262 | - | |
263 | - boolean compress = false; | |
264 | - int format = -1; | |
265 | - int dataType = -1; | |
266 | - | |
267 | - switch (fmt){ | |
244 | + private static AndroidGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException { | |
245 | + AndroidGLImageFormat imageFormat = new AndroidGLImageFormat(); | |
246 | + switch (fmt) { | |
268 | 247 | case RGBA16: |
269 | 248 | case RGB16: |
270 | 249 | case RGB10: |
@@ -273,69 +252,110 @@ public class TextureUtil { | ||
273 | 252 | case Alpha16: |
274 | 253 | case Depth32: |
275 | 254 | case Depth32F: |
276 | - throw new UnsupportedOperationException("The image format '" | |
255 | + throw new UnsupportedOperationException("The image format '" | |
277 | 256 | + fmt + "' is not supported by OpenGL ES 2.0 specification."); |
278 | 257 | case Alpha8: |
279 | - format = GLES20.GL_ALPHA; | |
280 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
258 | + imageFormat.format = GLES20.GL_ALPHA; | |
259 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
281 | 260 | break; |
282 | 261 | case Luminance8: |
283 | - format = GLES20.GL_LUMINANCE; | |
284 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
262 | + imageFormat.format = GLES20.GL_LUMINANCE; | |
263 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
285 | 264 | break; |
286 | 265 | case Luminance8Alpha8: |
287 | - format = GLES20.GL_LUMINANCE_ALPHA; | |
288 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
266 | + imageFormat.format = GLES20.GL_LUMINANCE_ALPHA; | |
267 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
289 | 268 | break; |
290 | 269 | case RGB565: |
291 | - format = GLES20.GL_RGB; | |
292 | - dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; | |
270 | + imageFormat.format = GLES20.GL_RGB; | |
271 | + imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; | |
293 | 272 | break; |
294 | 273 | case ARGB4444: |
295 | - format = GLES20.GL_RGBA4; | |
296 | - dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; | |
274 | + imageFormat.format = GLES20.GL_RGBA4; | |
275 | + imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; | |
297 | 276 | break; |
298 | 277 | case RGB5A1: |
299 | - format = GLES20.GL_RGBA; | |
300 | - dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; | |
278 | + imageFormat.format = GLES20.GL_RGBA; | |
279 | + imageFormat.dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; | |
301 | 280 | break; |
302 | 281 | case RGB8: |
303 | - format = GLES20.GL_RGB; | |
304 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
282 | + imageFormat.format = GLES20.GL_RGB; | |
283 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
305 | 284 | break; |
306 | 285 | case BGR8: |
307 | - format = GLES20.GL_RGB; | |
308 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
286 | + imageFormat.format = GLES20.GL_RGB; | |
287 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
309 | 288 | break; |
310 | 289 | case RGBA8: |
311 | - format = GLES20.GL_RGBA; | |
312 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
290 | + imageFormat.format = GLES20.GL_RGBA; | |
291 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
313 | 292 | break; |
314 | 293 | case Depth: |
315 | 294 | case Depth16: |
316 | 295 | case Depth24: |
317 | - format = GLES20.GL_DEPTH_COMPONENT; | |
318 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
296 | + imageFormat.format = GLES20.GL_DEPTH_COMPONENT; | |
297 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
319 | 298 | break; |
320 | 299 | case DXT1: |
321 | 300 | if (!DXT1) { |
322 | 301 | unsupportedFormat(fmt); |
323 | 302 | } |
324 | - format = 0x83F0; | |
325 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
326 | - compress = true; | |
303 | + imageFormat.format = 0x83F0; | |
304 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
305 | + imageFormat.compress = true; | |
327 | 306 | break; |
328 | 307 | case DXT1A: |
329 | 308 | if (!DXT1) { |
330 | 309 | unsupportedFormat(fmt); |
331 | 310 | } |
332 | - format = 0x83F1; | |
333 | - dataType = GLES20.GL_UNSIGNED_BYTE; | |
334 | - compress = true; | |
311 | + imageFormat.format = 0x83F1; | |
312 | + imageFormat.dataType = GLES20.GL_UNSIGNED_BYTE; | |
313 | + imageFormat.compress = true; | |
335 | 314 | break; |
336 | 315 | default: |
337 | 316 | throw new UnsupportedOperationException("Unrecognized format: " + fmt); |
338 | 317 | } |
318 | + return imageFormat; | |
319 | + } | |
320 | + | |
321 | + private static class AndroidGLImageFormat { | |
322 | + | |
323 | + boolean compress = false; | |
324 | + int format = -1; | |
325 | + int dataType = -1; | |
326 | + } | |
327 | + | |
328 | + private static void uploadTexture(Image img, | |
329 | + int target, | |
330 | + int index) { | |
331 | + | |
332 | + if (img.getEfficentData() instanceof AndroidImageInfo) { | |
333 | + throw new RendererException("This image uses efficient data. " | |
334 | + + "Use uploadTextureBitmap instead."); | |
335 | + } | |
336 | + | |
337 | + // Otherwise upload image directly. | |
338 | + // Prefer to only use power of 2 textures here to avoid errors. | |
339 | + Image.Format fmt = img.getFormat(); | |
340 | + ByteBuffer data; | |
341 | + if (index >= 0 || img.getData() != null && img.getData().size() > 0) { | |
342 | + data = img.getData(index); | |
343 | + } else { | |
344 | + data = null; | |
345 | + } | |
346 | + | |
347 | + int width = img.getWidth(); | |
348 | + int height = img.getHeight(); | |
349 | + | |
350 | + if (!NPOT) { | |
351 | + // Check if texture is POT | |
352 | + if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { | |
353 | + throw new RendererException("Non-power-of-2 textures " | |
354 | + + "are not supported by the video hardware " | |
355 | + + "and no scaling path available for image: " + img); | |
356 | + } | |
357 | + } | |
358 | + AndroidGLImageFormat imageFormat = getImageFormat(fmt); | |
339 | 359 | |
340 | 360 | if (data != null) { |
341 | 361 | GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); |
@@ -343,81 +363,134 @@ public class TextureUtil { | ||
343 | 363 | |
344 | 364 | int[] mipSizes = img.getMipMapSizes(); |
345 | 365 | int pos = 0; |
346 | - if (mipSizes == null){ | |
347 | - if (data != null) | |
348 | - mipSizes = new int[]{ data.capacity() }; | |
349 | - else | |
350 | - mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 }; | |
366 | + if (mipSizes == null) { | |
367 | + if (data != null) { | |
368 | + mipSizes = new int[]{data.capacity()}; | |
369 | + } else { | |
370 | + mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; | |
371 | + } | |
351 | 372 | } |
352 | 373 | |
353 | - // XXX: might want to change that when support | |
354 | - // of more than paletted compressions is added.. | |
355 | - /// NOTE: Doesn't support mipmaps | |
356 | -// if (compress){ | |
357 | -// data.clear(); | |
358 | -// GLES20.glCompressedTexImage2D(target, | |
359 | -// 1 - mipSizes.length, | |
360 | -// format, | |
361 | -// width, | |
362 | -// height, | |
363 | -// 0, | |
364 | -// data.capacity(), | |
365 | -// data); | |
366 | -// return; | |
367 | -// } | |
368 | - | |
369 | - for (int i = 0; i < mipSizes.length; i++){ | |
370 | - int mipWidth = Math.max(1, width >> i); | |
374 | + for (int i = 0; i < mipSizes.length; i++) { | |
375 | + int mipWidth = Math.max(1, width >> i); | |
371 | 376 | int mipHeight = Math.max(1, height >> i); |
372 | -// int mipDepth = Math.max(1, depth >> i); | |
373 | 377 | |
374 | - if (data != null){ | |
378 | + if (data != null) { | |
375 | 379 | data.position(pos); |
376 | 380 | data.limit(pos + mipSizes[i]); |
377 | 381 | } |
378 | 382 | |
379 | - if (compress && data != null){ | |
383 | + if (imageFormat.compress && data != null) { | |
380 | 384 | GLES20.glCompressedTexImage2D(target, |
381 | - i, | |
382 | - format, | |
383 | - mipWidth, | |
384 | - mipHeight, | |
385 | - 0, | |
386 | - data.remaining(), | |
387 | - data); | |
388 | - }else{ | |
385 | + i, | |
386 | + imageFormat.format, | |
387 | + mipWidth, | |
388 | + mipHeight, | |
389 | + 0, | |
390 | + data.remaining(), | |
391 | + data); | |
392 | + } else { | |
389 | 393 | GLES20.glTexImage2D(target, |
390 | - i, | |
391 | - format, | |
392 | - mipWidth, | |
393 | - mipHeight, | |
394 | - 0, | |
395 | - format, | |
396 | - dataType, | |
397 | - data); | |
394 | + i, | |
395 | + imageFormat.format, | |
396 | + mipWidth, | |
397 | + mipHeight, | |
398 | + 0, | |
399 | + imageFormat.format, | |
400 | + imageFormat.dataType, | |
401 | + data); | |
398 | 402 | } |
399 | 403 | |
400 | 404 | pos += mipSizes[i]; |
401 | 405 | } |
402 | 406 | } |
403 | 407 | |
408 | + private static void checkGLError() { | |
409 | + int error; | |
410 | + while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { | |
411 | + throw new RendererException("OpenGL Error " + error); | |
412 | + } | |
413 | + } | |
414 | + | |
404 | 415 | /** |
405 | - * Update the texture currently bound to target at with data from the given Image at position x and y. The parameter | |
406 | - * index is used as the zoffset in case a 3d texture or texture 2d array is being updated. | |
416 | + * Update the texture currently bound to target at with data from the given | |
417 | + * Image at position x and y. The parameter index is used as the zoffset in | |
418 | + * case a 3d texture or texture 2d array is being updated. | |
407 | 419 | * |
408 | - * @param image Image with the source data (this data will be put into the texture) | |
420 | + * @param image Image with the source data (this data will be put into the | |
421 | + * texture) | |
409 | 422 | * @param target the target texture |
410 | 423 | * @param index the mipmap level to update |
411 | 424 | * @param x the x position where to put the image in the texture |
412 | 425 | * @param y the y position where to put the image in the texture |
413 | 426 | */ |
414 | 427 | public static void uploadSubTexture( |
415 | - Image image, | |
416 | - int target, | |
417 | - int index, | |
418 | - int x, | |
419 | - int y) { | |
420 | - // FIXME and implement this! | |
421 | - } | |
428 | + Image img, | |
429 | + int target, | |
430 | + int index, | |
431 | + int x, | |
432 | + int y) { | |
433 | + if (img.getEfficentData() instanceof AndroidImageInfo) { | |
434 | + AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData(); | |
435 | + uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y); | |
436 | + return; | |
437 | + } | |
438 | + | |
439 | + // Otherwise upload image directly. | |
440 | + // Prefer to only use power of 2 textures here to avoid errors. | |
441 | + Image.Format fmt = img.getFormat(); | |
442 | + ByteBuffer data; | |
443 | + if (index >= 0 || img.getData() != null && img.getData().size() > 0) { | |
444 | + data = img.getData(index); | |
445 | + } else { | |
446 | + data = null; | |
447 | + } | |
448 | + | |
449 | + int width = img.getWidth(); | |
450 | + int height = img.getHeight(); | |
451 | + | |
452 | + if (!NPOT) { | |
453 | + // Check if texture is POT | |
454 | + if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { | |
455 | + throw new RendererException("Non-power-of-2 textures " | |
456 | + + "are not supported by the video hardware " | |
457 | + + "and no scaling path available for image: " + img); | |
458 | + } | |
459 | + } | |
460 | + AndroidGLImageFormat imageFormat = getImageFormat(fmt); | |
461 | + | |
462 | + if (data != null) { | |
463 | + GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); | |
464 | + } | |
422 | 465 | |
466 | + int[] mipSizes = img.getMipMapSizes(); | |
467 | + int pos = 0; | |
468 | + if (mipSizes == null) { | |
469 | + if (data != null) { | |
470 | + mipSizes = new int[]{data.capacity()}; | |
471 | + } else { | |
472 | + mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8}; | |
473 | + } | |
474 | + } | |
475 | + | |
476 | + for (int i = 0; i < mipSizes.length; i++) { | |
477 | + int mipWidth = Math.max(1, width >> i); | |
478 | + int mipHeight = Math.max(1, height >> i); | |
479 | + | |
480 | + if (data != null) { | |
481 | + data.position(pos); | |
482 | + data.limit(pos + mipSizes[i]); | |
483 | + } | |
484 | + | |
485 | + if (imageFormat.compress && data != null) { | |
486 | + GLES20.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data); | |
487 | + checkGLError(); | |
488 | + } else { | |
489 | + GLES20.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data); | |
490 | + checkGLError(); | |
491 | + } | |
492 | + | |
493 | + pos += mipSizes[i]; | |
494 | + } | |
495 | + } | |
423 | 496 | } |