1package com.jme3.renderer.android;
2
3import android.graphics.Bitmap;
4import android.opengl.GLES20;
5import android.opengl.GLUtils;
6import com.jme3.asset.AndroidImageInfo;
7import com.jme3.math.FastMath;
8import com.jme3.texture.Image;
9import com.jme3.texture.Image.Format;
10import java.nio.ByteBuffer;
11import javax.microedition.khronos.opengles.GL10;
12
13public class TextureUtil {
14
15    public static int convertTextureFormat(Format fmt){
16        switch (fmt){
17            case Alpha16:
18            case Alpha8:
19                return GL10.GL_ALPHA;
20            case Luminance8Alpha8:
21            case Luminance16Alpha16:
22                return GL10.GL_LUMINANCE_ALPHA;
23            case Luminance8:
24            case Luminance16:
25                return GL10.GL_LUMINANCE;
26            case RGB10:
27            case RGB16:
28            case BGR8:
29            case RGB8:
30            case RGB565:
31                return GL10.GL_RGB;
32            case RGB5A1:
33            case RGBA16:
34            case RGBA8:
35                return GL10.GL_RGBA;
36
37            case Depth:
38                return GLES20.GL_DEPTH_COMPONENT;
39            case Depth16:
40                return GLES20.GL_DEPTH_COMPONENT16;
41            case Depth24:
42            case Depth32:
43            case Depth32F:
44                throw new UnsupportedOperationException("Unsupported depth format: " + fmt);
45
46            case DXT1A:
47                throw new UnsupportedOperationException("Unsupported format: " + fmt);
48            default:
49                throw new UnsupportedOperationException("Unrecognized format: " + fmt);
50        }
51    }
52
53    private static void buildMipmap(Bitmap bitmap) {
54        int level = 0;
55        int height = bitmap.getHeight();
56        int width = bitmap.getWidth();
57
58        while (height >= 1 || width >= 1) {
59            //First of all, generate the texture from our bitmap and set it to the according level
60            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
61
62            if (height == 1 || width == 1) {
63                break;
64            }
65
66            //Increase the mipmap level
67            level++;
68
69            height /= 2;
70            width /= 2;
71            Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
72
73            //bitmap.recycle();
74            bitmap = bitmap2;
75        }
76    }
77
78    /**
79     * <code>uploadTextureBitmap</code> uploads a native android bitmap
80     * @param target
81     * @param bitmap
82     * @param generateMips
83     * @param powerOf2
84     */
85    public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2)
86    {
87        if (!powerOf2)
88        {
89            int width = bitmap.getWidth();
90            int height = bitmap.getHeight();
91            if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height))
92            {
93                // scale to power of two
94                width = FastMath.nearestPowerOfTwo(width);
95                height = FastMath.nearestPowerOfTwo(height);
96                Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
97                //bitmap.recycle();
98                bitmap = bitmap2;
99            }
100        }
101
102        if (generateMips)
103        {
104            buildMipmap(bitmap);
105        }
106        else
107        {
108            GLUtils.texImage2D(target, 0, bitmap, 0);
109            //bitmap.recycle();
110        }
111    }
112
113    public static void uploadTexture(
114                                     Image img,
115                                     int target,
116                                     int index,
117                                     int border,
118                                     boolean tdc,
119                                     boolean generateMips,
120                                     boolean powerOf2){
121
122        if (img.getEfficentData() instanceof AndroidImageInfo){
123            // If image was loaded from asset manager, use fast path
124            AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
125            uploadTextureBitmap(target, imageInfo.getBitmap(), generateMips, powerOf2);
126            return;
127        }
128
129        // Otherwise upload image directly.
130        // Prefer to only use power of 2 textures here to avoid errors.
131
132        Image.Format fmt = img.getFormat();
133        ByteBuffer data;
134        if (index >= 0 || img.getData() != null && img.getData().size() > 0){
135            data = img.getData(index);
136        }else{
137            data = null;
138        }
139
140        int width = img.getWidth();
141        int height = img.getHeight();
142        int depth = img.getDepth();
143
144        boolean compress = false;
145        int internalFormat = -1;
146        int format = -1;
147        int dataType = -1;
148
149        switch (fmt){
150            case Alpha16:
151            case Alpha8:
152                format = GLES20.GL_ALPHA;
153                dataType = GLES20.GL_UNSIGNED_BYTE;
154                break;
155            case Luminance8:
156                format = GLES20.GL_LUMINANCE;
157                dataType = GLES20.GL_UNSIGNED_BYTE;
158                break;
159            case Luminance8Alpha8:
160                format = GLES20.GL_LUMINANCE_ALPHA;
161                dataType = GLES20.GL_UNSIGNED_BYTE;
162                break;
163            case Luminance16Alpha16:
164                format = GLES20.GL_LUMINANCE_ALPHA;
165                dataType = GLES20.GL_UNSIGNED_BYTE;
166                break;
167            case Luminance16:
168                format = GLES20.GL_LUMINANCE;
169                dataType = GLES20.GL_UNSIGNED_BYTE;
170                break;
171            case RGB565:
172                format = GLES20.GL_RGB;
173                internalFormat = GLES20.GL_RGB565;
174                dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5;
175                break;
176            case ARGB4444:
177                format = GLES20.GL_RGBA;
178                dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4;
179                break;
180            case RGB10:
181                format = GLES20.GL_RGB;
182                dataType = GLES20.GL_UNSIGNED_BYTE;
183                break;
184            case RGB16:
185                format = GLES20.GL_RGB;
186                dataType = GLES20.GL_UNSIGNED_BYTE;
187                break;
188            case RGB5A1:
189                format = GLES20.GL_RGBA;
190                internalFormat = GLES20.GL_RGB5_A1;
191                dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1;
192                break;
193            case RGB8:
194                format = GLES20.GL_RGB;
195                dataType = GLES20.GL_UNSIGNED_BYTE;
196                break;
197            case BGR8:
198                format = GLES20.GL_RGB;
199                dataType = GLES20.GL_UNSIGNED_BYTE;
200                break;
201            case RGBA16:
202                format = GLES20.GL_RGBA;
203                internalFormat = GLES20.GL_RGBA4;
204                dataType = GLES20.GL_UNSIGNED_BYTE;
205                break;
206            case RGBA8:
207                format = GLES20.GL_RGBA;
208                dataType = GLES20.GL_UNSIGNED_BYTE;
209                break;
210            case DXT1A:
211                format = GLES20.GL_COMPRESSED_TEXTURE_FORMATS;
212                dataType = GLES20.GL_UNSIGNED_BYTE;
213            case Depth:
214                format = GLES20.GL_DEPTH_COMPONENT;
215                dataType = GLES20.GL_UNSIGNED_BYTE;
216                break;
217            case Depth16:
218                format = GLES20.GL_DEPTH_COMPONENT;
219                internalFormat = GLES20.GL_DEPTH_COMPONENT16;
220                dataType = GLES20.GL_UNSIGNED_BYTE;
221                break;
222            case Depth24:
223            case Depth32:
224            case Depth32F:
225                throw new UnsupportedOperationException("Unsupported depth format: " + fmt);
226            default:
227                throw new UnsupportedOperationException("Unrecognized format: " + fmt);
228        }
229
230        if (internalFormat == -1)
231        {
232            internalFormat = format;
233        }
234
235        if (data != null)
236            GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
237
238        int[] mipSizes = img.getMipMapSizes();
239        int pos = 0;
240        if (mipSizes == null){
241            if (data != null)
242                mipSizes = new int[]{ data.capacity() };
243            else
244                mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
245        }
246
247        // XXX: might want to change that when support
248        // of more than paletted compressions is added..
249        if (compress){
250            data.clear();
251            GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
252                                      1 - mipSizes.length,
253                                      format,
254                                      width,
255                                      height,
256                                      0,
257                                      data.capacity(),
258                                      data);
259            return;
260        }
261
262        for (int i = 0; i < mipSizes.length; i++){
263            int mipWidth =  Math.max(1, width  >> i);
264            int mipHeight = Math.max(1, height >> i);
265            int mipDepth =  Math.max(1, depth  >> i);
266
267            if (data != null){
268                data.position(pos);
269                data.limit(pos + mipSizes[i]);
270            }
271
272            if (compress && data != null){
273                GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D,
274                                          i,
275                                          format,
276                                          mipWidth,
277                                          mipHeight,
278                                          0,
279                                          data.remaining(),
280                                          data);
281            }else{
282                GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D,
283                                i,
284                                internalFormat,
285                                mipWidth,
286                                mipHeight,
287                                0,
288                                format,
289                                dataType,
290                                data);
291            }
292
293            pos += mipSizes[i];
294        }
295    }
296
297}
298