Mega Code Archive

 
Categories / Android / 2D Graphics
 

Demonstrate how to use ETC1 format compressed textures

/*  * Copyright (C) 2008 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package app.test; import static android.opengl.GLES10.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.ETC1Util; import android.opengl.GLES10; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.opengl.GLUtils; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; /**  * A GLSurfaceView.Renderer that uses the Android-specific  * android.opengl.GLESXXX static OpenGL ES APIs. The static APIs expose more of  * the OpenGL ES features than the javax.microedition.khronos.opengles APIs, and  * also provide a programming model that is closer to the C OpenGL ES APIs,  * which may make it easier to reuse code and documentation written for the C  * OpenGL ES APIs.  *   */ class StaticTriangleRenderer implements GLSurfaceView.Renderer {   public interface TextureLoader {     /**      * Load a texture into the currently bound OpenGL texture.      */     void load(GL10 gl);   }   public StaticTriangleRenderer(Context context) {     init(context, new RobotTextureLoader());   }   public StaticTriangleRenderer(Context context, TextureLoader loader) {     init(context, loader);   }   private void init(Context context, TextureLoader loader) {     mContext = context;     mTriangle = new Triangle();     mTextureLoader = loader;   }   public void onSurfaceCreated(GL10 gl, EGLConfig config) {     /*      * By default, OpenGL enables features that improve quality but reduce      * performance. One might want to tweak that especially on software      * renderer.      */     glDisable(GL_DITHER);     /*      * Some one-time OpenGL initialization can be made here probably based      * on features of this particular context      */     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);     glClearColor(.5f, .5f, .5f, 1);     glShadeModel(GL_SMOOTH);     glEnable(GL_DEPTH_TEST);     glEnable(GL_TEXTURE_2D);     /*      * Create our texture. This has to be done each time the surface is      * created.      */     int[] textures = new int[1];     glGenTextures(1, textures, 0);     mTextureID = textures[0];     glBindTexture(GL_TEXTURE_2D, mTextureID);     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);     mTextureLoader.load(gl);   }   public void onDrawFrame(GL10 gl) {     /*      * By default, OpenGL enables features that improve quality but reduce      * performance. One might want to tweak that especially on software      * renderer.      */     glDisable(GL_DITHER);     glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);     /*      * Usually, the first thing one might want to do is to clear the screen.      * The most efficient way of doing this is to use glClear().      */     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     /*      * Now we're ready to draw some 3D objects      */     glMatrixMode(GL_MODELVIEW);     glLoadIdentity();     GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);     glEnableClientState(GL_VERTEX_ARRAY);     glEnableClientState(GL_TEXTURE_COORD_ARRAY);     glActiveTexture(GL_TEXTURE0);     glBindTexture(GL_TEXTURE_2D, mTextureID);     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);     long time = SystemClock.uptimeMillis() % 4000L;     float angle = 0.090f * ((int) time);     glRotatef(angle, 0, 0, 1.0f);     mTriangle.draw(gl);   }   public void onSurfaceChanged(GL10 gl, int w, int h) {     glViewport(0, 0, w, h);     /*      * Set our projection matrix. This doesn't have to be done each time we      * draw, but usually a new projection needs to be set when the viewport      * is resized.      */     float ratio = (float) w / h;     glMatrixMode(GL_PROJECTION);     glLoadIdentity();     glFrustumf(-ratio, ratio, -1, 1, 3, 7);   }   private Context mContext;   private Triangle mTriangle;   private int mTextureID;   private TextureLoader mTextureLoader;   private class RobotTextureLoader implements TextureLoader {     public void load(GL10 gl) {       InputStream is = mContext.getResources().openRawResource(           R.raw.robot);       Bitmap bitmap;       try {         bitmap = BitmapFactory.decodeStream(is);       } finally {         try {           is.close();         } catch (IOException e) {           // Ignore.         }       }       GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);       bitmap.recycle();     }   }   static class Triangle {     public Triangle() {       // Buffers to be passed to gl*Pointer() functions       // must be direct, i.e., they must be placed on the       // native heap where the garbage collector cannot       // move them.       //       // Buffers with multi-byte datatypes (e.g., short, int, float)       // must have their byte order set to native order       ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);       vbb.order(ByteOrder.nativeOrder());       mFVertexBuffer = vbb.asFloatBuffer();       ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);       tbb.order(ByteOrder.nativeOrder());       mTexBuffer = tbb.asFloatBuffer();       ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);       ibb.order(ByteOrder.nativeOrder());       mIndexBuffer = ibb.asShortBuffer();       // A unit-sided equilateral triangle centered on the origin.       float[] coords = {           // X, Y, Z           -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 };       for (int i = 0; i < VERTS; i++) {         for (int j = 0; j < 3; j++) {           mFVertexBuffer.put(coords[i * 3 + j] * 2.0f);         }       }       for (int i = 0; i < VERTS; i++) {         for (int j = 0; j < 2; j++) {           mTexBuffer.put(coords[i * 3 + j] * 2.0f + 0.5f);         }       }       for (int i = 0; i < VERTS; i++) {         mIndexBuffer.put((short) i);       }       mFVertexBuffer.position(0);       mTexBuffer.position(0);       mIndexBuffer.position(0);     }     public void draw(GL10 gl) {       glFrontFace(GL_CCW);       glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);       glEnable(GL_TEXTURE_2D);       glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);       glDrawElements(GL_TRIANGLE_STRIP, VERTS, GL_UNSIGNED_SHORT,           mIndexBuffer);     }     private final static int VERTS = 3;     private FloatBuffer mFVertexBuffer;     private FloatBuffer mTexBuffer;     private ShortBuffer mIndexBuffer;   } } /**  * Demonstrate how to use ETC1 format compressed textures. This sample can be  * recompiled to use either resource-based textures (compressed offline using  * the etc1tool), or textures created on the fly by compressing images.  *   */ public class Test extends Activity {   private final static String TAG = "CompressedTextureActivity";   /**    * Choose between creating a compressed texture on the fly or loading a    * compressed texture from a resource.    */   private final static boolean TEST_CREATE_TEXTURE = false;   /**    * When creating a compressed texture on the fly, choose whether or not to    * use the i/o stream APIs.    */   private final static boolean USE_STREAM_IO = false;   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     mGLView = new GLSurfaceView(this);     mGLView.setEGLConfigChooser(false);     StaticTriangleRenderer.TextureLoader loader;     if (TEST_CREATE_TEXTURE) {       loader = new SyntheticCompressedTextureLoader();     } else {       loader = new CompressedTextureLoader();     }     mGLView.setRenderer(new StaticTriangleRenderer(this, loader));     setContentView(mGLView);   }   @Override   protected void onPause() {     super.onPause();     mGLView.onPause();   }   @Override   protected void onResume() {     super.onResume();     mGLView.onResume();   }   /**    * Demonstrate how to load a compressed texture from an APK resource.    *     */   private class CompressedTextureLoader implements       StaticTriangleRenderer.TextureLoader {     public void load(GL10 gl) {       Log.w(TAG, "ETC1 texture support: " + ETC1Util.isETC1Supported());       InputStream input = getResources().openRawResource(R.raw.androids);       try {         ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0, GLES10.GL_RGB,             GLES10.GL_UNSIGNED_SHORT_5_6_5, input);       } catch (IOException e) {         Log.w(TAG, "Could not load texture: " + e);       } finally {         try {           input.close();         } catch (IOException e) {           // ignore exception thrown from close.         }       }     }   }   /**    * Demonstrate how to create a compressed texture on the fly.    */   private class SyntheticCompressedTextureLoader implements       StaticTriangleRenderer.TextureLoader {     public void load(GL10 gl) {       int width = 128;       int height = 128;       Buffer image = createImage(width, height);       ETC1Util.ETC1Texture etc1Texture = ETC1Util.compressTexture(image,           width, height, 3, 3 * width);       if (USE_STREAM_IO) {         // Test the ETC1Util APIs for reading and writing compressed         // textures to I/O streams.         try {           ByteArrayOutputStream bos = new ByteArrayOutputStream();           ETC1Util.writeTexture(etc1Texture, bos);           ByteArrayInputStream bis = new ByteArrayInputStream(               bos.toByteArray());           ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0,               GLES10.GL_RGB, GLES10.GL_UNSIGNED_SHORT_5_6_5, bis);         } catch (IOException e) {           Log.w(TAG, "Could not load texture: " + e);         }       } else {         ETC1Util.loadTexture(GLES10.GL_TEXTURE_2D, 0, 0, GLES10.GL_RGB,             GLES10.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);       }     }     private Buffer createImage(int width, int height) {       int stride = 3 * width;       ByteBuffer image = ByteBuffer.allocateDirect(height * stride)           .order(ByteOrder.nativeOrder());       // Fill with a pretty "munching squares" pattern:       for (int t = 0; t < height; t++) {         byte red = (byte) (255 - 2 * t);         byte green = (byte) (2 * t);         byte blue = 0;         for (int x = 0; x < width; x++) {           int y = x ^ t;           image.position(stride * y + x * 3);           image.put(red);           image.put(green);           image.put(blue);         }       }       image.position(0);       return image;     }   }   private GLSurfaceView mGLView; }