2D Graphics Android

/*
 * 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;
}