Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2010 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.android.camera.ui;
     18 
     19 import com.android.camera.Util;
     20 
     21 import android.app.Activity;
     22 import android.content.Context;
     23 import android.graphics.Color;
     24 import android.graphics.Matrix;
     25 import android.graphics.PixelFormat;
     26 import android.opengl.GLSurfaceView;
     27 import android.opengl.GLU;
     28 import android.os.SystemClock;
     29 import android.util.AttributeSet;
     30 import android.util.DisplayMetrics;
     31 import android.util.Log;
     32 import android.view.MotionEvent;
     33 import android.view.animation.Animation;
     34 import android.view.animation.Transformation;
     35 
     36 import java.nio.ByteBuffer;
     37 import java.nio.ByteOrder;
     38 import java.util.ArrayList;
     39 import java.util.Stack;
     40 
     41 import javax.microedition.khronos.egl.EGLConfig;
     42 import javax.microedition.khronos.opengles.GL10;
     43 import javax.microedition.khronos.opengles.GL11;
     44 import javax.microedition.khronos.opengles.GL11Ext;
     45 
     46 // The root component of all <code>GLView</code>s. The rendering is done in GL
     47 // thread while the event handling is done in the main thread.  To synchronize
     48 // the two threads, the entry points of this package need to synchronize on the
     49 // <code>GLRootView</code> instance unless it can be proved that the rendering
     50 // thread won't access the same thing as the method. The entry points include:
     51 // (1) The public methods of HeadUpDisplay
     52 // (2) The public methods of CameraHeadUpDisplay
     53 // (3) The overridden methods in GLRootView.
     54 public class GLRootView extends GLSurfaceView
     55         implements GLSurfaceView.Renderer {
     56     private static final String TAG = "GLRootView";
     57 
     58     private final boolean ENABLE_FPS_TEST = false;
     59     private int mFrameCount = 0;
     60     private long mFrameCountingStart = 0;
     61 
     62     // We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
     63     private static final int VERTEX_BUFFER_SIZE = 16 * 2;
     64 
     65     // We need 22 indices for a normal nine-patch image
     66     private static final int INDEX_BUFFER_SIZE = 22;
     67 
     68     private static final int FLAG_INITIALIZED = 1;
     69     private static final int FLAG_NEED_LAYOUT = 2;
     70 
     71     private static boolean mTexture2DEnabled;
     72 
     73     private static float sPixelDensity = -1f;
     74 
     75     private GL11 mGL;
     76     private GLView mContentView;
     77     private DisplayMetrics mDisplayMetrics;
     78 
     79     private final ArrayList<Animation> mAnimations = new ArrayList<Animation>();
     80 
     81     private final Stack<Transformation> mFreeTransform =
     82             new Stack<Transformation>();
     83 
     84     private final Transformation mTransformation = new Transformation();
     85     private final Stack<Transformation> mTransformStack =
     86             new Stack<Transformation>();
     87 
     88     private float mLastAlpha = mTransformation.getAlpha();
     89 
     90     private final float mMatrixValues[] = new float[16];
     91 
     92     private final float mUvBuffer[] = new float[VERTEX_BUFFER_SIZE];
     93     private final float mXyBuffer[] = new float[VERTEX_BUFFER_SIZE];
     94     private final byte mIndexBuffer[] = new byte[INDEX_BUFFER_SIZE];
     95 
     96     private int mNinePatchX[] = new int[4];
     97     private int mNinePatchY[] = new int[4];
     98     private float mNinePatchU[] = new float[4];
     99     private float mNinePatchV[] = new float[4];
    100 
    101     private ByteBuffer mXyPointer;
    102     private ByteBuffer mUvPointer;
    103     private ByteBuffer mIndexPointer;
    104 
    105     private int mFlags = FLAG_NEED_LAYOUT;
    106     private long mAnimationTime;
    107 
    108     private CameraEGLConfigChooser mEglConfigChooser = new CameraEGLConfigChooser();
    109 
    110     public GLRootView(Context context) {
    111         this(context, null);
    112     }
    113 
    114     public GLRootView(Context context, AttributeSet attrs) {
    115         super(context, attrs);
    116         initialize();
    117     }
    118 
    119     void registerLaunchedAnimation(Animation animation) {
    120         // Register the newly launched animation so that we can set the start
    121         // time more precisely. (Usually, it takes much longer for the first
    122         // rendering, so we set the animation start time as the time we
    123         // complete rendering)
    124         mAnimations.add(animation);
    125     }
    126 
    127     public long currentAnimationTimeMillis() {
    128         return mAnimationTime;
    129     }
    130 
    131     public synchronized static float dpToPixel(Context context, float dp) {
    132         if (sPixelDensity < 0) {
    133             DisplayMetrics metrics = new DisplayMetrics();
    134             ((Activity) context).getWindowManager()
    135                     .getDefaultDisplay().getMetrics(metrics);
    136             sPixelDensity =  metrics.density;
    137         }
    138         return sPixelDensity * dp;
    139     }
    140 
    141     public static int dpToPixel(Context context, int dp) {
    142         return (int)(dpToPixel(context, (float) dp) + .5f);
    143     }
    144 
    145     public Transformation obtainTransformation() {
    146         if (!mFreeTransform.isEmpty()) {
    147             Transformation t = mFreeTransform.pop();
    148             t.clear();
    149             return t;
    150         }
    151         return new Transformation();
    152     }
    153 
    154     public void freeTransformation(Transformation freeTransformation) {
    155         mFreeTransform.push(freeTransformation);
    156     }
    157 
    158     public Transformation getTransformation() {
    159         return mTransformation;
    160     }
    161 
    162     public Transformation pushTransform() {
    163         Transformation trans = obtainTransformation();
    164         trans.set(mTransformation);
    165         mTransformStack.push(trans);
    166         return mTransformation;
    167     }
    168 
    169     public void popTransform() {
    170         Transformation trans = mTransformStack.pop();
    171         mTransformation.set(trans);
    172         freeTransformation(trans);
    173     }
    174 
    175     public CameraEGLConfigChooser getEGLConfigChooser() {
    176         return mEglConfigChooser;
    177     }
    178 
    179     private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
    180         return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
    181     }
    182 
    183     private void initialize() {
    184         mFlags |= FLAG_INITIALIZED;
    185         setEGLConfigChooser(mEglConfigChooser);
    186         getHolder().setFormat(PixelFormat.TRANSLUCENT);
    187         setZOrderOnTop(true);
    188 
    189         setRenderer(this);
    190 
    191         int size = VERTEX_BUFFER_SIZE * Float.SIZE / Byte.SIZE;
    192         mXyPointer = allocateDirectNativeOrderBuffer(size);
    193         mUvPointer = allocateDirectNativeOrderBuffer(size);
    194         mIndexPointer = allocateDirectNativeOrderBuffer(INDEX_BUFFER_SIZE);
    195     }
    196 
    197     public void setContentPane(GLView content) {
    198         mContentView = content;
    199         content.onAttachToRoot(this);
    200 
    201         // no parent for the content pane
    202         content.onAddToParent(null);
    203         requestLayoutContentPane();
    204     }
    205 
    206     public GLView getContentPane() {
    207         return mContentView;
    208     }
    209 
    210     void handleLowMemory() {
    211         //TODO: delete texture from GL
    212     }
    213 
    214     public synchronized void requestLayoutContentPane() {
    215         if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
    216 
    217         // "View" system will invoke onLayout() for initialization(bug ?), we
    218         // have to ignore it since the GLThread is not ready yet.
    219         if ((mFlags & FLAG_INITIALIZED) == 0) return;
    220 
    221         mFlags |= FLAG_NEED_LAYOUT;
    222         requestRender();
    223     }
    224 
    225     private synchronized void layoutContentPane() {
    226         mFlags &= ~FLAG_NEED_LAYOUT;
    227         int width = getWidth();
    228         int height = getHeight();
    229         Log.v(TAG, "layout content pane " + width + "x" + height);
    230         if (mContentView != null && width != 0 && height != 0) {
    231             mContentView.layout(0, 0, width, height);
    232         }
    233     }
    234 
    235     @Override
    236     protected void onLayout(
    237             boolean changed, int left, int top, int right, int bottom) {
    238         if (changed) requestLayoutContentPane();
    239     }
    240 
    241     /**
    242      * Called when the context is created, possibly after automatic destruction.
    243      */
    244     // This is a GLSurfaceView.Renderer callback
    245     public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
    246         GL11 gl = (GL11) gl1;
    247         if (mGL != null) {
    248             // The GL Object has changed
    249             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
    250         }
    251         mGL = gl;
    252 
    253         if (!ENABLE_FPS_TEST) {
    254             setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    255         } else {
    256             setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    257         }
    258 
    259         // Disable unused state
    260         gl.glDisable(GL11.GL_LIGHTING);
    261 
    262         // Enable used features
    263         gl.glEnable(GL11.GL_BLEND);
    264         gl.glEnable(GL11.GL_SCISSOR_TEST);
    265         gl.glEnable(GL11.GL_STENCIL_TEST);
    266         gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    267         gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    268         gl.glEnable(GL11.GL_TEXTURE_2D);
    269         mTexture2DEnabled = true;
    270 
    271         gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
    272                 GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
    273 
    274         // Set the background color
    275         gl.glClearColor(0f, 0f, 0f, 0f);
    276         gl.glClearStencil(0);
    277 
    278         gl.glVertexPointer(2, GL11.GL_FLOAT, 0, mXyPointer);
    279         gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, mUvPointer);
    280 
    281     }
    282 
    283     /**
    284      * Called when the OpenGL surface is recreated without destroying the
    285      * context.
    286      */
    287     // This is a GLSurfaceView.Renderer callback
    288     public void onSurfaceChanged(GL10 gl1, int width, int height) {
    289         Log.v(TAG, "onSurfaceChanged: " + width + "x" + height
    290                 + ", gl10: " + gl1.toString());
    291         GL11 gl = (GL11) gl1;
    292         mGL = gl;
    293         gl.glViewport(0, 0, width, height);
    294 
    295         gl.glMatrixMode(GL11.GL_PROJECTION);
    296         gl.glLoadIdentity();
    297 
    298         GLU.gluOrtho2D(gl, 0, width, 0, height);
    299         Matrix matrix = mTransformation.getMatrix();
    300         matrix.reset();
    301         matrix.preTranslate(0, getHeight());
    302         matrix.preScale(1, -1);
    303     }
    304 
    305     private void setAlphaValue(float alpha) {
    306         if (mLastAlpha == alpha) return;
    307 
    308         GL11 gl = mGL;
    309         mLastAlpha = alpha;
    310         if (alpha >= 0.95f) {
    311             gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
    312                     GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
    313         } else {
    314             gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
    315                     GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
    316             gl.glColor4f(alpha, alpha, alpha, alpha);
    317         }
    318     }
    319 
    320     public void drawRect(int x, int y, int width, int height) {
    321         float matrix[] = mMatrixValues;
    322         mTransformation.getMatrix().getValues(matrix);
    323         drawRect(x, y, width, height, matrix);
    324     }
    325 
    326     private static void putRectangle(float x, float y,
    327             float width, float height, float[] buffer, ByteBuffer pointer) {
    328         buffer[0] = x;
    329         buffer[1] = y;
    330         buffer[2] = x + width;
    331         buffer[3] = y;
    332         buffer[4] = x;
    333         buffer[5] = y + height;
    334         buffer[6] = x + width;
    335         buffer[7] = y + height;
    336         pointer.asFloatBuffer().put(buffer, 0, 8).position(0);
    337     }
    338 
    339     private void drawRect(
    340             int x, int y, int width, int height, float matrix[]) {
    341         GL11 gl = mGL;
    342         gl.glPushMatrix();
    343         gl.glMultMatrixf(toGLMatrix(matrix), 0);
    344         putRectangle(x, y, width, height, mXyBuffer, mXyPointer);
    345         gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
    346         gl.glPopMatrix();
    347     }
    348 
    349     public void drawNinePatch(
    350             NinePatchTexture tex, int x, int y, int width, int height) {
    351 
    352         NinePatchChunk chunk = tex.getNinePatchChunk();
    353 
    354         // The code should be easily extended to handle the general cases by
    355         // allocating more space for buffers. But let's just handle the only
    356         // use case.
    357         if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) {
    358             throw new RuntimeException("unsupported nine patch");
    359         }
    360         if (!tex.bind(this, mGL)) {
    361             throw new RuntimeException("cannot bind" + tex.toString());
    362         }
    363         if (width <= 0 || height <= 0) return ;
    364 
    365         int divX[] = mNinePatchX;
    366         int divY[] = mNinePatchY;
    367         float divU[] = mNinePatchU;
    368         float divV[] = mNinePatchV;
    369 
    370         int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
    371         int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
    372 
    373         setAlphaValue(mTransformation.getAlpha());
    374         Matrix matrix = mTransformation.getMatrix();
    375         matrix.getValues(mMatrixValues);
    376         GL11 gl = mGL;
    377         gl.glPushMatrix();
    378         gl.glMultMatrixf(toGLMatrix(mMatrixValues), 0);
    379         gl.glTranslatef(x, y, 0);
    380         drawMesh(divX, divY, divU, divV, nx, ny);
    381         gl.glPopMatrix();
    382     }
    383 
    384     /**
    385      * Stretches the texture according to the nine-patch rules. It will
    386      * linearly distribute the strechy parts defined in the nine-patch chunk to
    387      * the target area.
    388      *
    389      * <pre>
    390      *                      source
    391      *          /--------------^---------------\
    392      *         u0    u1       u2  u3     u4   u5
    393      * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
    394      *          |    div0    div1 div2   div3  |
    395      *          |     |       /   /      /    /
    396      *          |     |      /   /     /    /
    397      *          |     |     /   /    /    /
    398      *          |fffff|ssss|fff|sss|ffff| ---> x
    399      *         x0    x1   x2  x3  x4   x5
    400      *          \----------v------------/
    401      *                  target
    402      *
    403      * f: fixed segment
    404      * s: stretchy segment
    405      * </pre>
    406      *
    407      * @param div the stretch parts defined in nine-patch chunk
    408      * @param source the length of the texture
    409      * @param target the length on the drawing plan
    410      * @param u output, the positions of these dividers in the texture
    411      *        coordinate
    412      * @param x output, the corresponding position of these dividers on the
    413      *        drawing plan
    414      * @return the number of these dividers.
    415      */
    416     private int stretch(
    417             int x[], float u[], int div[], int source, int target) {
    418         int textureSize = Util.nextPowerOf2(source);
    419         float textureBound = (source - 0.5f) / textureSize;
    420 
    421         int stretch = 0;
    422         for (int i = 0, n = div.length; i < n; i += 2) {
    423             stretch += div[i + 1] - div[i];
    424         }
    425 
    426         float remaining = target - source + stretch;
    427 
    428         int lastX = 0;
    429         int lastU = 0;
    430 
    431         x[0] = 0;
    432         u[0] = 0;
    433         for (int i = 0, n = div.length; i < n; i += 2) {
    434             // fixed segment
    435             x[i + 1] = lastX + (div[i] - lastU);
    436             u[i + 1] = Math.min((float) div[i] / textureSize, textureBound);
    437 
    438             // stretchy segment
    439             float partU = div[i + 1] - div[i];
    440             int partX = (int)(remaining * partU / stretch + 0.5f);
    441             remaining -= partX;
    442             stretch -= partU;
    443 
    444             lastX = x[i + 1] + partX;
    445             lastU = div[i + 1];
    446             x[i + 2] = lastX;
    447             u[i + 2] = Math.min((float) lastU / textureSize, textureBound);
    448         }
    449         // the last fixed segment
    450         x[div.length + 1] = target;
    451         u[div.length + 1] = textureBound;
    452 
    453         // remove segments with length 0.
    454         int last = 0;
    455         for (int i = 1, n = div.length + 2; i < n; ++i) {
    456             if (x[last] == x[i]) continue;
    457             x[++last] = x[i];
    458             u[last] = u[i];
    459         }
    460         return last + 1;
    461     }
    462 
    463     private void drawMesh(
    464             int x[], int y[], float u[], float v[], int nx, int ny) {
    465         /*
    466          * Given a 3x3 nine-patch image, the vertex order is defined as the
    467          * following graph:
    468          *
    469          * (0) (1) (2) (3)
    470          *  |  /|  /|  /|
    471          *  | / | / | / |
    472          * (4) (5) (6) (7)
    473          *  | \ | \ | \ |
    474          *  |  \|  \|  \|
    475          * (8) (9) (A) (B)
    476          *  |  /|  /|  /|
    477          *  | / | / | / |
    478          * (C) (D) (E) (F)
    479          *
    480          * And we draw the triangle strip in the following index order:
    481          *
    482          * index: 04152637B6A5948C9DAEBF
    483          */
    484         int pntCount = 0;
    485         float xy[] = mXyBuffer;
    486         float uv[] = mUvBuffer;
    487         for (int j = 0; j < ny; ++j) {
    488             for (int i = 0; i < nx; ++i) {
    489                 int xIndex = (pntCount++) << 1;
    490                 int yIndex = xIndex + 1;
    491                 xy[xIndex] = x[i];
    492                 xy[yIndex] = y[j];
    493                 uv[xIndex] = u[i];
    494                 uv[yIndex] = v[j];
    495             }
    496         }
    497         mUvPointer.asFloatBuffer().put(uv, 0, pntCount << 1).position(0);
    498         mXyPointer.asFloatBuffer().put(xy, 0, pntCount << 1).position(0);
    499 
    500         int idxCount = 1;
    501         byte index[] = mIndexBuffer;
    502         for (int i = 0, bound = nx * (ny - 1); true;) {
    503             // normal direction
    504             --idxCount;
    505             for (int j = 0; j < nx; ++j, ++i) {
    506                 index[idxCount++] = (byte) i;
    507                 index[idxCount++] = (byte) (i + nx);
    508             }
    509             if (i >= bound) break;
    510 
    511             // reverse direction
    512             int sum = i + i + nx - 1;
    513             --idxCount;
    514             for (int j = 0; j < nx; ++j, ++i) {
    515                 index[idxCount++] = (byte) (sum - i);
    516                 index[idxCount++] = (byte) (sum - i + nx);
    517             }
    518             if (i >= bound) break;
    519         }
    520         mIndexPointer.put(index, 0, idxCount).position(0);
    521 
    522         mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
    523                 idxCount, GL11.GL_UNSIGNED_BYTE, mIndexPointer);
    524     }
    525 
    526     private float[] mapPoints(Matrix matrix, int x1, int y1, int x2, int y2) {
    527         float[] point = mXyBuffer;
    528         point[0] = x1; point[1] = y1; point[2] = x2; point[3] = y2;
    529         matrix.mapPoints(point, 0, point, 0, 4);
    530         return point;
    531     }
    532 
    533     public void clipRect(int x, int y, int width, int height) {
    534         float point[] = mapPoints(
    535                 mTransformation.getMatrix(), x, y + height, x + width, y);
    536 
    537         // mMatrix could be a rotation matrix. In this case, we need to find
    538         // the boundaries after rotation. (only handle 90 * n degrees)
    539         if (point[0] > point[2]) {
    540             x = (int) point[2];
    541             width = (int) point[0] - x;
    542         } else {
    543             x = (int) point[0];
    544             width = (int) point[2] - x;
    545         }
    546         if (point[1] > point[3]) {
    547             y = (int) point[3];
    548             height = (int) point[1] - y;
    549         } else {
    550             y = (int) point[1];
    551             height = (int) point[3] - y;
    552         }
    553         mGL.glScissor(x, y, width, height);
    554     }
    555 
    556     public void clearClip() {
    557         mGL.glScissor(0, 0, getWidth(), getHeight());
    558     }
    559 
    560     private static float[] toGLMatrix(float v[]) {
    561         v[15] = v[8]; v[13] = v[5]; v[5] = v[4]; v[4] = v[1];
    562         v[12] = v[2]; v[1] = v[3]; v[3] = v[6];
    563         v[2] = v[6] = v[8] = v[9] = 0;
    564         v[10] = 1;
    565         return v;
    566     }
    567 
    568     public void drawColor(int x, int y, int width, int height, int color) {
    569         float alpha = mTransformation.getAlpha();
    570         GL11 gl = mGL;
    571         if (mTexture2DEnabled) {
    572             // Set mLastAlpha to an invalid value, so that it will reset again
    573             // in setAlphaValue(float) later.
    574             mLastAlpha = -1.0f;
    575             gl.glDisable(GL11.GL_TEXTURE_2D);
    576             mTexture2DEnabled = false;
    577         }
    578         alpha /= 256.0f;
    579         gl.glColor4f(Color.red(color) * alpha, Color.green(color) * alpha,
    580                 Color.blue(color) * alpha, Color.alpha(color) * alpha);
    581         drawRect(x, y, width, height);
    582     }
    583 
    584     public void drawTexture(
    585             BasicTexture texture, int x, int y, int width, int height) {
    586         drawTexture(texture, x, y, width, height, mTransformation.getAlpha());
    587     }
    588 
    589     public void drawTexture(BasicTexture texture,
    590             int x, int y, int width, int height, float alpha) {
    591 
    592         if (!mTexture2DEnabled) {
    593             mGL.glEnable(GL11.GL_TEXTURE_2D);
    594             mTexture2DEnabled = true;
    595         }
    596 
    597         if (!texture.bind(this, mGL)) {
    598             throw new RuntimeException("cannot bind" + texture.toString());
    599         }
    600         if (width <= 0 || height <= 0) return ;
    601 
    602         Matrix matrix = mTransformation.getMatrix();
    603         matrix.getValues(mMatrixValues);
    604 
    605         // Test whether it has been rotated or flipped, if so, glDrawTexiOES
    606         // won't work
    607         if (isMatrixRotatedOrFlipped(mMatrixValues)) {
    608             putRectangle(0, 0,
    609                     (texture.mWidth - 0.5f) / texture.mTextureWidth,
    610                     (texture.mHeight - 0.5f) / texture.mTextureHeight,
    611                     mUvBuffer, mUvPointer);
    612             setAlphaValue(alpha);
    613             drawRect(x, y, width, height, mMatrixValues);
    614         } else {
    615             // draw the rect from bottom-left to top-right
    616             float points[] = mapPoints(matrix, x, y + height, x + width, y);
    617             x = (int) points[0];
    618             y = (int) points[1];
    619             width = (int) points[2] - x;
    620             height = (int) points[3] - y;
    621             if (width > 0 && height > 0) {
    622                 setAlphaValue(alpha);
    623                 ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
    624             }
    625         }
    626     }
    627 
    628     private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
    629         return matrix[Matrix.MSKEW_X] != 0 || matrix[Matrix.MSKEW_Y] != 0
    630                 || matrix[Matrix.MSCALE_X] < 0 || matrix[Matrix.MSCALE_Y] > 0;
    631     }
    632 
    633     public synchronized void onDrawFrame(GL10 gl) {
    634         if (ENABLE_FPS_TEST) {
    635             long now = System.nanoTime();
    636             if (mFrameCountingStart == 0) {
    637                 mFrameCountingStart = now;
    638             } else if ((now - mFrameCountingStart) > 1000000000) {
    639                 Log.v(TAG, "fps: " + (double) mFrameCount
    640                         * 1000000000 / (now - mFrameCountingStart));
    641                 mFrameCountingStart = now;
    642                 mFrameCount = 0;
    643             }
    644             ++mFrameCount;
    645         }
    646 
    647         if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
    648         clearClip();
    649         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
    650         gl.glEnable(GL11.GL_BLEND);
    651         gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
    652 
    653         mAnimationTime = SystemClock.uptimeMillis();
    654         if (mContentView != null) {
    655             mContentView.render(GLRootView.this, (GL11) gl);
    656         }
    657         long now = SystemClock.uptimeMillis();
    658         for (Animation animation : mAnimations) {
    659             animation.setStartTime(now);
    660         }
    661         mAnimations.clear();
    662     }
    663 
    664     @Override
    665     public synchronized boolean dispatchTouchEvent(MotionEvent event) {
    666         // If this has been detached from root, we don't need to handle event
    667         return mContentView != null
    668                 ? mContentView.dispatchTouchEvent(event)
    669                 : false;
    670     }
    671 
    672     public DisplayMetrics getDisplayMetrics() {
    673         if (mDisplayMetrics == null) {
    674             mDisplayMetrics = new DisplayMetrics();
    675             ((Activity) getContext()).getWindowManager()
    676                     .getDefaultDisplay().getMetrics(mDisplayMetrics);
    677         }
    678         return mDisplayMetrics;
    679     }
    680 
    681     public void copyTexture2D(
    682             RawTexture texture, int x, int y, int width, int height)
    683             throws GLOutOfMemoryException {
    684         Matrix matrix = mTransformation.getMatrix();
    685         matrix.getValues(mMatrixValues);
    686 
    687         if (isMatrixRotatedOrFlipped(mMatrixValues)) {
    688             throw new IllegalArgumentException("cannot support rotated matrix");
    689         }
    690         float points[] = mapPoints(matrix, x, y + height, x + width, y);
    691         x = (int) points[0];
    692         y = (int) points[1];
    693         width = (int) points[2] - x;
    694         height = (int) points[3] - y;
    695 
    696         GL11 gl = mGL;
    697         int newWidth = Util.nextPowerOf2(width);
    698         int newHeight = Util.nextPowerOf2(height);
    699         int glError = GL11.GL_NO_ERROR;
    700 
    701         gl.glBindTexture(GL11.GL_TEXTURE_2D, texture.getId());
    702 
    703         int[] cropRect = {0,  0, width, height};
    704         gl.glTexParameteriv(GL11.GL_TEXTURE_2D,
    705                 GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
    706         gl.glTexParameteri(GL11.GL_TEXTURE_2D,
    707                 GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
    708         gl.glTexParameteri(GL11.GL_TEXTURE_2D,
    709                 GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
    710         gl.glTexParameterf(GL11.GL_TEXTURE_2D,
    711                 GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    712         gl.glTexParameterf(GL11.GL_TEXTURE_2D,
    713                 GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    714         gl.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0,
    715                 GL11.GL_RGBA, x, y, newWidth, newHeight, 0);
    716         glError = gl.glGetError();
    717 
    718         if (glError == GL11.GL_OUT_OF_MEMORY) {
    719             throw new GLOutOfMemoryException();
    720         }
    721 
    722         if (glError != GL11.GL_NO_ERROR) {
    723             throw new RuntimeException(
    724                     "Texture copy fail, glError " + glError);
    725         }
    726 
    727         texture.setSize(width, height);
    728         texture.setTextureSize(newWidth, newHeight);
    729     }
    730 
    731 }
    732