• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Main repository of MikuMikuStudio


Commit MetaInfo

Revision8cf668e1e18989c6fe4077e4a7676c3964477937 (tree)
Time2013-03-16 05:45:36
Authorremy.bouquet@gmail.com <remy.bouquet@gmai...>
Commiterremy.bouquet@gmail.com

Log Message

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

Change Summary

Incremental Difference

--- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java
+++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
@@ -18,14 +18,13 @@ import java.util.logging.Logger;
1818 public class TextureUtil {
1919
2020 private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
21-
2221 //TODO Make this configurable through appSettings
2322 public static boolean ENABLE_COMPRESSION = true;
2423 private static boolean NPOT = false;
2524 private static boolean ETC1support = false;
2625 private static boolean DXT1 = false;
2726 private static boolean DEPTH24 = false;
28-
27+
2928 public static void loadTextureFeatures(String extensionString) {
3029 ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
3130 DEPTH24 = extensionString.contains("GL_OES_depth24");
@@ -36,21 +35,21 @@ public class TextureUtil {
3635 logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
3736 logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
3837 }
39-
38+
4039 private static void buildMipmap(Bitmap bitmap, boolean compress) {
4140 int level = 0;
4241 int height = bitmap.getHeight();
4342 int width = bitmap.getWidth();
44-
43+
4544 logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
4645
4746 GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
48-
47+
4948 while (height >= 1 || width >= 1) {
5049 //First of all, generate the texture from our bitmap and set it to the according level
5150 if (compress) {
5251 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);
5453 } else {
5554 logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
5655 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, level, bitmap, 0);
@@ -67,20 +66,26 @@ public class TextureUtil {
6766
6867 // Recycle any bitmaps created as a result of scaling the bitmap.
6968 // Do not recycle the original image (mipmap level 0)
70- if (level != 0){
69+ if (level != 0) {
7170 bitmap.recycle();
7271 }
73-
72+
7473 bitmap = bitmap2;
75-
74+
7675 level++;
7776 }
7877 }
7978
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) {
8180 if (bitmap.hasAlpha()) {
8281 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+ }
8489 } else {
8590 // Convert to RGB565
8691 int bytesPerPixel = 2;
@@ -97,15 +102,15 @@ public class TextureUtil {
97102 // Encode the image into the output bytebuffer
98103 int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
99104 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+
106111 // Delete the input image buffer
107112 BufferUtils.destroyDirectBuffer(inputImage);
108-
113+
109114 // Create an ETC1Texture from the compressed image data
110115 ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
111116
@@ -113,55 +118,53 @@ public class TextureUtil {
113118 if (bytesPerPixel == 2) {
114119 int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
115120 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+
126144 // ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
127145 // GLES20.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
128146 // } else if (bytesPerPixel == 3) {
129147 // ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB,
130148 // GLES20.GL_UNSIGNED_BYTE, etc1Texture);
131149 }
132-
150+
133151 BufferUtils.destroyDirectBuffer(compressedImage);
134152 }
135153 }
136-
154+
137155 /**
138156 * <code>uploadTextureBitmap</code> uploads a native android bitmap
139157 */
140158 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) {
141166 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
165168
166169 boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
167170 if (needMips && willCompress) {
@@ -172,29 +175,39 @@ public class TextureUtil {
172175 if (willCompress) {
173176 // Image is compressed but mipmaps are not desired, upload directly.
174177 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+
176180 } else {
177181 // 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+
183195 if (needMips) {
184196 // No pregenerated mips available,
185197 // generate from base level if required
186198 GLES20.glGenerateMipmap(target);
199+ checkGLError();
187200 }
188201 }
189202 }
190-
203+
191204 if (recycleBitmap) {
192205 bitmap.recycle();
193206 }
194207 }
195-
208+
196209 public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
197- if (img.getEfficentData() instanceof AndroidImageInfo){
210+ if (img.getEfficentData() instanceof AndroidImageInfo) {
198211 logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
199212 // If image was loaded from asset manager, use fast path
200213 AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
@@ -206,14 +219,14 @@ public class TextureUtil {
206219 logger.log(Level.WARNING, "Generating mipmaps is only"
207220 + " supported for Bitmap based or non-compressed images!");
208221 }
209-
222+
210223 // 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."));
215228 uploadTexture(img, target, index);
216-
229+
217230 // Image was uploaded using slower path, since it is not compressed,
218231 // then compress it
219232 if (wantGeneratedMips) {
@@ -223,48 +236,14 @@ public class TextureUtil {
223236 }
224237 }
225238 }
226-
239+
227240 private static void unsupportedFormat(Format fmt) {
228241 throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
229242 }
230243
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) {
268247 case RGBA16:
269248 case RGB16:
270249 case RGB10:
@@ -273,69 +252,110 @@ public class TextureUtil {
273252 case Alpha16:
274253 case Depth32:
275254 case Depth32F:
276- throw new UnsupportedOperationException("The image format '"
255+ throw new UnsupportedOperationException("The image format '"
277256 + fmt + "' is not supported by OpenGL ES 2.0 specification.");
278257 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;
281260 break;
282261 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;
285264 break;
286265 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;
289268 break;
290269 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;
293272 break;
294273 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;
297276 break;
298277 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;
301280 break;
302281 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;
305284 break;
306285 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;
309288 break;
310289 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;
313292 break;
314293 case Depth:
315294 case Depth16:
316295 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;
319298 break;
320299 case DXT1:
321300 if (!DXT1) {
322301 unsupportedFormat(fmt);
323302 }
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;
327306 break;
328307 case DXT1A:
329308 if (!DXT1) {
330309 unsupportedFormat(fmt);
331310 }
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;
335314 break;
336315 default:
337316 throw new UnsupportedOperationException("Unrecognized format: " + fmt);
338317 }
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);
339359
340360 if (data != null) {
341361 GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
@@ -343,81 +363,134 @@ public class TextureUtil {
343363
344364 int[] mipSizes = img.getMipMapSizes();
345365 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+ }
351372 }
352373
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);
371376 int mipHeight = Math.max(1, height >> i);
372-// int mipDepth = Math.max(1, depth >> i);
373377
374- if (data != null){
378+ if (data != null) {
375379 data.position(pos);
376380 data.limit(pos + mipSizes[i]);
377381 }
378382
379- if (compress && data != null){
383+ if (imageFormat.compress && data != null) {
380384 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 {
389393 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);
398402 }
399403
400404 pos += mipSizes[i];
401405 }
402406 }
403407
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+
404415 /**
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.
407419 *
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)
409422 * @param target the target texture
410423 * @param index the mipmap level to update
411424 * @param x the x position where to put the image in the texture
412425 * @param y the y position where to put the image in the texture
413426 */
414427 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+ }
422465
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+ }
423496 }