• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GLRootView(Context context)110     public GLRootView(Context context) {
111         this(context, null);
112     }
113 
GLRootView(Context context, AttributeSet attrs)114     public GLRootView(Context context, AttributeSet attrs) {
115         super(context, attrs);
116         initialize();
117     }
118 
registerLaunchedAnimation(Animation animation)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 
currentAnimationTimeMillis()127     public long currentAnimationTimeMillis() {
128         return mAnimationTime;
129     }
130 
dpToPixel(Context context, float dp)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 
dpToPixel(Context context, int dp)141     public static int dpToPixel(Context context, int dp) {
142         return (int)(dpToPixel(context, (float) dp) + .5f);
143     }
144 
obtainTransformation()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 
freeTransformation(Transformation freeTransformation)154     public void freeTransformation(Transformation freeTransformation) {
155         mFreeTransform.push(freeTransformation);
156     }
157 
getTransformation()158     public Transformation getTransformation() {
159         return mTransformation;
160     }
161 
pushTransform()162     public Transformation pushTransform() {
163         Transformation trans = obtainTransformation();
164         trans.set(mTransformation);
165         mTransformStack.push(trans);
166         return mTransformation;
167     }
168 
popTransform()169     public void popTransform() {
170         Transformation trans = mTransformStack.pop();
171         mTransformation.set(trans);
172         freeTransformation(trans);
173     }
174 
getEGLConfigChooser()175     public CameraEGLConfigChooser getEGLConfigChooser() {
176         return mEglConfigChooser;
177     }
178 
allocateDirectNativeOrderBuffer(int size)179     private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
180         return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
181     }
182 
initialize()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 
setContentPane(GLView content)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 
getContentPane()206     public GLView getContentPane() {
207         return mContentView;
208     }
209 
handleLowMemory()210     void handleLowMemory() {
211         //TODO: delete texture from GL
212     }
213 
requestLayoutContentPane()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 
layoutContentPane()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
onLayout( boolean changed, int left, int top, int right, int bottom)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
onSurfaceCreated(GL10 gl1, EGLConfig config)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
onSurfaceChanged(GL10 gl1, int width, int height)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 
setAlphaValue(float alpha)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 
drawRect(int x, int y, int width, int height)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 
putRectangle(float x, float y, float width, float height, float[] buffer, ByteBuffer pointer)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 
drawRect( int x, int y, int width, int height, float matrix[])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 
drawNinePatch( NinePatchTexture tex, int x, int y, int width, int height)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      */
stretch( int x[], float u[], int div[], int source, int target)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 
drawMesh( int x[], int y[], float u[], float v[], int nx, int ny)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 
mapPoints(Matrix matrix, int x1, int y1, int x2, int y2)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 
clipRect(int x, int y, int width, int height)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 
clearClip()556     public void clearClip() {
557         mGL.glScissor(0, 0, getWidth(), getHeight());
558     }
559 
toGLMatrix(float v[])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 
drawColor(int x, int y, int width, int height, int color)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 
drawTexture( BasicTexture texture, int x, int y, int width, int height)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 
drawTexture(BasicTexture texture, int x, int y, int width, int height, float alpha)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 
isMatrixRotatedOrFlipped(float matrix[])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 
onDrawFrame(GL10 gl)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
dispatchTouchEvent(MotionEvent event)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 
getDisplayMetrics()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 
copyTexture2D( RawTexture texture, int x, int y, int width, int height)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