Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.cooliris.media;
     18 
     19 import java.nio.ByteBuffer;
     20 import java.nio.ByteOrder;
     21 import java.nio.CharBuffer;
     22 import java.nio.FloatBuffer;
     23 
     24 import javax.microedition.khronos.opengles.GL10;
     25 import javax.microedition.khronos.opengles.GL11;
     26 
     27 /**
     28  * A 2D rectangular mesh. Can be drawn textured or untextured. This version is
     29  * modified from the original Grid.java (found in the SpriteText package in the
     30  * APIDemos Android sample) to support hardware vertex buffers.
     31  */
     32 
     33 final class GridQuad {
     34     private FloatBuffer mVertexBuffer;
     35     private FloatBuffer mOverlayTexCoordBuffer;
     36     private CharBuffer mIndexBuffer;
     37 
     38     private int mW;
     39     private int mH;
     40     private static final int INDEX_COUNT = 4;
     41     private static final int ORIENTATION_COUNT = 360;
     42     private int mVertBufferIndex;
     43     private int mIndexBufferIndex;
     44     private int mOverlayTextureCoordBufferIndex;
     45     private boolean mDynamicVBO;
     46     private float mU;
     47     private float mV;
     48     private float mAnimU;
     49     private float mAnimV;
     50     private float mWidth;
     51     private float mHeight;
     52     private float mAnimWidth;
     53     private float mAnimHeight;
     54     private boolean mQuadChanged;
     55     private float mDefaultAspectRatio;
     56     private int mBaseTextureCoordBufferIndex;
     57     private FloatBuffer mBaseTexCoordBuffer;
     58     private final boolean mOrientedQuad;
     59     private MatrixStack mMatrix;
     60     private float[] mCoordsIn = new float[4];
     61     private float[] mCoordsOut = new float[4];
     62 
     63     public static GridQuad createGridQuad(float width, float height, float xOffset, float yOffset, float uExtents, float vExtents,
     64             boolean generateOrientedQuads) {
     65         // generateOrientedQuads = false;
     66         GridQuad grid = new GridQuad(generateOrientedQuads);
     67         grid.mWidth = width;
     68         grid.mHeight = height;
     69         grid.mAnimWidth = width;
     70         grid.mAnimHeight = height;
     71         grid.mDefaultAspectRatio = width / height;
     72         float widthBy2 = width * 0.5f;
     73         float heightBy2 = height * 0.5f;
     74         final float v = vExtents;
     75         final float u = uExtents;
     76         if (!generateOrientedQuads) {
     77             grid.set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v);
     78             grid.set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v);
     79             grid.set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f);
     80             grid.set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f);
     81         } else {
     82             for (int i = 0; i < ORIENTATION_COUNT; ++i) {
     83                 grid.set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v, true, i);
     84                 grid.set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v, true, i);
     85                 grid.set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f, true, i);
     86                 grid.set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f, true, i);
     87             }
     88         }
     89         grid.mU = uExtents;
     90         grid.mV = uExtents;
     91         return grid;
     92     }
     93 
     94     public GridQuad(boolean generateOrientedQuads) {
     95         mOrientedQuad = generateOrientedQuads;
     96         if (mOrientedQuad) {
     97             mMatrix = new MatrixStack();
     98             mMatrix.glLoadIdentity();
     99         }
    100         int vertsAcross = 2;
    101         int vertsDown = 2;
    102         mW = vertsAcross;
    103         mH = vertsDown;
    104         int size = vertsAcross * vertsDown;
    105         final int FLOAT_SIZE = 4;
    106         final int CHAR_SIZE = 2;
    107         final int orientationCount = (!generateOrientedQuads) ? 1 : ORIENTATION_COUNT;
    108         mVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3 * orientationCount).order(ByteOrder.nativeOrder())
    109                 .asFloatBuffer();
    110         mOverlayTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2 * orientationCount).order(ByteOrder.nativeOrder())
    111                 .asFloatBuffer();
    112         mBaseTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2 * orientationCount).order(ByteOrder.nativeOrder())
    113                 .asFloatBuffer();
    114 
    115         int indexCount = INDEX_COUNT; // using tristrips
    116         mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount * orientationCount).order(ByteOrder.nativeOrder())
    117                 .asCharBuffer();
    118 
    119         /*
    120          * Initialize triangle list mesh.
    121          *
    122          * [0]-----[ 1] ... | / | | / | | / | [w]-----[w+1] ... | |
    123          */
    124         CharBuffer buffer = mIndexBuffer;
    125         for (int i = 0; i < INDEX_COUNT * orientationCount; ++i) {
    126             buffer.put(i, (char) i);
    127         }
    128         mVertBufferIndex = 0;
    129     }
    130 
    131     public void setDynamic(boolean dynamic) {
    132         mDynamicVBO = dynamic;
    133         if (mOrientedQuad) {
    134             throw new UnsupportedOperationException("Dynamic Quads can't have orientations");
    135         }
    136     }
    137 
    138     public float getWidth() {
    139         return mWidth;
    140     }
    141 
    142     public float getHeight() {
    143         return mHeight;
    144     }
    145 
    146     public void update(float timeElapsed) {
    147         mAnimWidth = FloatUtils.animate(mAnimWidth, mWidth, timeElapsed);
    148         mAnimHeight = FloatUtils.animate(mAnimHeight, mHeight, timeElapsed);
    149         mAnimU = FloatUtils.animate(mAnimU, mU, timeElapsed);
    150         mAnimV = FloatUtils.animate(mAnimV, mV, timeElapsed);
    151         recomputeQuad();
    152     }
    153 
    154     public void commit() {
    155         mAnimWidth = mWidth;
    156         mAnimHeight = mHeight;
    157         mAnimU = mU;
    158         mAnimV = mV;
    159     }
    160 
    161     public void recomputeQuad() {
    162         mVertexBuffer.clear();
    163         mBaseTexCoordBuffer.clear();
    164         float widthBy2 = mAnimWidth * 0.5f;
    165         float heightBy2 = mAnimHeight * 0.5f;
    166         float xOffset = 0.0f;
    167         float yOffset = 0.0f;
    168         float u = mU;
    169         float v = mV;
    170         set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v, false, 0);
    171         set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v, false, 0);
    172         set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f, false, 0);
    173         set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f, false, 0);
    174         mQuadChanged = true;
    175     }
    176 
    177     public void resizeQuad(float viewAspect, float u, float v, float imageWidth, float imageHeight) {
    178         // given the u,v; we know the aspect ratio of the image
    179         // we have to change one of the co-ords depending upon the image and
    180         // viewport aspect ratio
    181         mU = u;
    182         mV = v;
    183         float imageAspect = imageWidth / imageHeight;
    184         float width = mDefaultAspectRatio;
    185         float height = 1.0f;
    186         if (viewAspect < 1.0f) {
    187             height = height * (mDefaultAspectRatio / imageAspect);
    188             float maxHeight = width / viewAspect;
    189             if (height > maxHeight) {
    190                 // we need to reduce the width and height proportionately
    191                 float ratio = height / maxHeight;
    192                 height /= ratio;
    193                 width /= ratio;
    194             }
    195         } else {
    196             width = width * (imageAspect / mDefaultAspectRatio);
    197             float maxWidth = height * viewAspect;
    198             if (width > maxWidth) {
    199                 float ratio = width / maxWidth;
    200                 width /= ratio;
    201                 height /= ratio;
    202             }
    203         }
    204         mWidth = width;
    205         mHeight = height;
    206         commit();
    207         recomputeQuad();
    208     }
    209 
    210     public void set(int i, int j, float x, float y, float z, float u, float v) {
    211         set(i, j, x, y, z, u, v, true, 0);
    212     }
    213 
    214     private void set(int i, int j, float x, float y, float z, float u, float v, boolean modifyOverlay, int orientationId) {
    215         if (i < 0 || i >= mW) {
    216             throw new IllegalArgumentException("i");
    217         }
    218         if (j < 0 || j >= mH) {
    219             throw new IllegalArgumentException("j");
    220         }
    221         int index = orientationId * INDEX_COUNT + mW * j + i;
    222         int posIndex = index * 3;
    223         mVertexBuffer.put(posIndex, x);
    224         mVertexBuffer.put(posIndex + 1, y);
    225         mVertexBuffer.put(posIndex + 2, z);
    226         int baseTexIndex = index * 2;
    227         // we can calculate the u,v for the orientation here
    228         MatrixStack matrix = mMatrix;
    229         if (matrix != null) {
    230             orientationId *= 2;
    231             matrix.glLoadIdentity();
    232             matrix.glTranslatef(0.5f, 0.5f, 0.0f);
    233             float itheta = (float) Math.toRadians(orientationId);
    234             float sini = (float) Math.sin(itheta);
    235             float scale = 1.0f + (sini * sini) * 0.33333333f;
    236             scale = 1.0f / scale;
    237             matrix.glRotatef(-orientationId, 0.0f, 0.0f, 1.0f);
    238             matrix.glScalef(scale, scale, 1.0f);
    239             matrix.glTranslatef(-0.5f + (float) (sini * 0.125f / scale), -0.5f
    240                     + (float) (Math.abs(Math.sin(itheta * 0.5f) * 0.25f)), 0.0f);
    241             // now we have the desired matrix
    242             // populate s,t,r,q
    243             // http://glprogramming.com/red/chapter09.html
    244             mCoordsIn[0] = u;
    245             mCoordsIn[1] = v;
    246             mCoordsIn[2] = 0.0f;
    247             mCoordsIn[3] = 1.0f;
    248             matrix.apply(mCoordsIn, mCoordsOut);
    249             u = mCoordsOut[0] / mCoordsOut[3];
    250             v = mCoordsOut[1] / mCoordsOut[3];
    251         }
    252         mBaseTexCoordBuffer.put(baseTexIndex, u);
    253         mBaseTexCoordBuffer.put(baseTexIndex + 1, v);
    254         if (modifyOverlay) {
    255             int texIndex = index * 2;
    256             mOverlayTexCoordBuffer.put(texIndex, u);
    257             mOverlayTexCoordBuffer.put(texIndex + 1, v);
    258         }
    259     }
    260 
    261     public void bindArrays(GL10 gl) {
    262         GL11 gl11 = (GL11) gl;
    263         // draw using hardware buffers
    264         gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
    265         gl11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0);
    266 
    267         gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mOverlayTextureCoordBufferIndex);
    268         if (mDynamicVBO && mQuadChanged) {
    269             final int texCoordSize = mOverlayTexCoordBuffer.capacity() * 4;
    270             mOverlayTexCoordBuffer.position(0);
    271             gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mOverlayTexCoordBuffer, GL11.GL_DYNAMIC_DRAW);
    272         }
    273         gl11.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
    274         gl11.glClientActiveTexture(GL11.GL_TEXTURE1);
    275         if (mDynamicVBO && mQuadChanged) {
    276             final int texCoordSize = mBaseTexCoordBuffer.capacity() * 4;
    277             mBaseTexCoordBuffer.position(0);
    278             gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mBaseTexCoordBuffer, GL11.GL_DYNAMIC_DRAW);
    279         }
    280         gl11.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
    281         gl11.glClientActiveTexture(GL11.GL_TEXTURE0);
    282         if (mDynamicVBO && mQuadChanged) {
    283             mQuadChanged = false;
    284             gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
    285             final int vertexSize = mVertexBuffer.capacity() * 4;
    286             mVertexBuffer.position(0);
    287             gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize, mVertexBuffer, GL11.GL_DYNAMIC_DRAW);
    288         }
    289         gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
    290     }
    291 
    292     public static final void draw(GL11 gl11, float orientationDegrees) {
    293         // don't call this method unless bindArrays was called
    294         int orientation = (int) Shared.normalizePositive(orientationDegrees);
    295         gl11.glDrawElements(GL11.GL_TRIANGLE_STRIP, INDEX_COUNT, GL11.GL_UNSIGNED_SHORT, orientation * INDEX_COUNT);
    296     }
    297 
    298     public void unbindArrays(GL10 gl) {
    299         GL11 gl11 = (GL11) gl;
    300         gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
    301         gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
    302     }
    303 
    304     public boolean usingHardwareBuffers() {
    305         return mVertBufferIndex != 0;
    306     }
    307 
    308     /**
    309      * When the OpenGL ES device is lost, GL handles become invalidated. In that
    310      * case, we just want to "forget" the old handles (without explicitly
    311      * deleting them) and make new ones.
    312      */
    313     public void forgetHardwareBuffers() {
    314         mVertBufferIndex = 0;
    315         mIndexBufferIndex = 0;
    316         mOverlayTextureCoordBufferIndex = 0;
    317     }
    318 
    319     /**
    320      * Deletes the hardware buffers allocated by this object (if any).
    321      */
    322     public void freeHardwareBuffers(GL10 gl) {
    323         if (mVertBufferIndex != 0) {
    324             if (gl instanceof GL11) {
    325                 GL11 gl11 = (GL11) gl;
    326                 int[] buffer = new int[1];
    327                 buffer[0] = mVertBufferIndex;
    328                 gl11.glDeleteBuffers(1, buffer, 0);
    329 
    330                 buffer[0] = mOverlayTextureCoordBufferIndex;
    331                 gl11.glDeleteBuffers(1, buffer, 0);
    332 
    333                 buffer[0] = mIndexBufferIndex;
    334                 gl11.glDeleteBuffers(1, buffer, 0);
    335             }
    336 
    337             forgetHardwareBuffers();
    338         }
    339     }
    340 
    341     /**
    342      * Allocates hardware buffers on the graphics card and fills them with data
    343      * if a buffer has not already been previously allocated. Note that this
    344      * function uses the GL_OES_vertex_buffer_object extension, which is not
    345      * guaranteed to be supported on every device.
    346      *
    347      * @param gl
    348      *            A pointer to the OpenGL ES context.
    349      */
    350 
    351     public void generateHardwareBuffers(GL10 gl) {
    352         if (mVertBufferIndex == 0) {
    353             if (gl instanceof GL11) {
    354                 GL11 gl11 = (GL11) gl;
    355                 int[] buffer = new int[1];
    356 
    357                 // Allocate and fill the vertex buffer.
    358                 gl11.glGenBuffers(1, buffer, 0);
    359                 mVertBufferIndex = buffer[0];
    360                 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
    361                 final int vertexSize = mVertexBuffer.capacity() * 4;
    362                 int bufferType = (mDynamicVBO) ? GL11.GL_DYNAMIC_DRAW : GL11.GL_STATIC_DRAW;
    363                 mVertexBuffer.position(0);
    364                 gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize, mVertexBuffer, bufferType);
    365 
    366                 // Allocate and fill the texture coordinate buffer.
    367                 gl11.glGenBuffers(1, buffer, 0);
    368                 mOverlayTextureCoordBufferIndex = buffer[0];
    369                 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mOverlayTextureCoordBufferIndex);
    370                 final int texCoordSize = mOverlayTexCoordBuffer.capacity() * 4;
    371                 mOverlayTexCoordBuffer.position(0);
    372                 gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mOverlayTexCoordBuffer, bufferType);
    373 
    374                 gl11.glGenBuffers(1, buffer, 0);
    375                 mBaseTextureCoordBufferIndex = buffer[0];
    376                 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBaseTextureCoordBufferIndex);
    377                 mBaseTexCoordBuffer.position(0);
    378                 gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mBaseTexCoordBuffer, bufferType);
    379 
    380                 // Unbind the array buffer.
    381                 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
    382 
    383                 // Allocate and fill the index buffer.
    384                 gl11.glGenBuffers(1, buffer, 0);
    385                 mIndexBufferIndex = buffer[0];
    386                 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
    387                 // A char is 2 bytes.
    388                 final int indexSize = mIndexBuffer.capacity() * 2;
    389                 mIndexBuffer.position(0);
    390                 gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, indexSize, mIndexBuffer, GL11.GL_STATIC_DRAW);
    391 
    392                 // Unbind the element array buffer.
    393                 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
    394             }
    395         }
    396     }
    397 
    398 }
    399