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
17package com.android.gallery3d.glrenderer;
18
19import android.graphics.Bitmap;
20import android.graphics.Rect;
21import android.graphics.RectF;
22import android.opengl.GLU;
23import android.opengl.GLUtils;
24import android.opengl.Matrix;
25import android.util.Log;
26
27import com.android.gallery3d.common.Utils;
28import com.android.gallery3d.util.IntArray;
29
30import java.nio.Buffer;
31import java.nio.ByteBuffer;
32import java.nio.ByteOrder;
33import java.nio.FloatBuffer;
34import java.util.ArrayList;
35
36import javax.microedition.khronos.opengles.GL10;
37import javax.microedition.khronos.opengles.GL11;
38import javax.microedition.khronos.opengles.GL11Ext;
39import javax.microedition.khronos.opengles.GL11ExtensionPack;
40
41public class GLES11Canvas implements GLCanvas {
42    @SuppressWarnings("unused")
43    private static final String TAG = "GLCanvasImp";
44
45    private static final float OPAQUE_ALPHA = 0.95f;
46
47    private static final int OFFSET_FILL_RECT = 0;
48    private static final int OFFSET_DRAW_LINE = 4;
49    private static final int OFFSET_DRAW_RECT = 6;
50    private static final float[] BOX_COORDINATES = {
51            0, 0, 1, 0, 0, 1, 1, 1,  // used for filling a rectangle
52            0, 0, 1, 1,              // used for drawing a line
53            0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
54
55    private GL11 mGL;
56
57    private final float mMatrixValues[] = new float[16];
58    private final float mTextureMatrixValues[] = new float[16];
59
60    // The results of mapPoints are stored in this buffer, and the order is
61    // x1, y1, x2, y2.
62    private final float mMapPointsBuffer[] = new float[4];
63
64    private final float mTextureColor[] = new float[4];
65
66    private int mBoxCoords;
67
68    private GLState mGLState;
69    private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
70
71    private float mAlpha;
72    private final ArrayList<ConfigState> mRestoreStack = new ArrayList<ConfigState>();
73    private ConfigState mRecycledRestoreAction;
74
75    private final RectF mDrawTextureSourceRect = new RectF();
76    private final RectF mDrawTextureTargetRect = new RectF();
77    private final float[] mTempMatrix = new float[32];
78    private final IntArray mUnboundTextures = new IntArray();
79    private final IntArray mDeleteBuffers = new IntArray();
80    private int mScreenWidth;
81    private int mScreenHeight;
82    private boolean mBlendEnabled = true;
83    private int mFrameBuffer[] = new int[1];
84    private static float[] sCropRect = new float[4];
85
86    private RawTexture mTargetTexture;
87
88    // Drawing statistics
89    int mCountDrawLine;
90    int mCountFillRect;
91    int mCountDrawMesh;
92    int mCountTextureRect;
93    int mCountTextureOES;
94
95    private static GLId mGLId = new GLES11IdImpl();
96
97    public GLES11Canvas(GL11 gl) {
98        mGL = gl;
99        mGLState = new GLState(gl);
100        // First create an nio buffer, then create a VBO from it.
101        int size = BOX_COORDINATES.length * Float.SIZE / Byte.SIZE;
102        FloatBuffer xyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
103        xyBuffer.put(BOX_COORDINATES, 0, BOX_COORDINATES.length).position(0);
104
105        int[] name = new int[1];
106        mGLId.glGenBuffers(1, name, 0);
107        mBoxCoords = name[0];
108
109        gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
110        gl.glBufferData(GL11.GL_ARRAY_BUFFER, xyBuffer.capacity() * (Float.SIZE / Byte.SIZE),
111                xyBuffer, GL11.GL_STATIC_DRAW);
112
113        gl.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
114        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
115
116        // Enable the texture coordinate array for Texture 1
117        gl.glClientActiveTexture(GL11.GL_TEXTURE1);
118        gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
119        gl.glClientActiveTexture(GL11.GL_TEXTURE0);
120        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
121
122        // mMatrixValues and mAlpha will be initialized in setSize()
123    }
124
125    @Override
126    public void setSize(int width, int height) {
127        Utils.assertTrue(width >= 0 && height >= 0);
128
129        if (mTargetTexture == null) {
130            mScreenWidth = width;
131            mScreenHeight = height;
132        }
133        mAlpha = 1.0f;
134
135        GL11 gl = mGL;
136        gl.glViewport(0, 0, width, height);
137        gl.glMatrixMode(GL11.GL_PROJECTION);
138        gl.glLoadIdentity();
139        GLU.gluOrtho2D(gl, 0, width, 0, height);
140
141        gl.glMatrixMode(GL11.GL_MODELVIEW);
142        gl.glLoadIdentity();
143
144        float matrix[] = mMatrixValues;
145        Matrix.setIdentityM(matrix, 0);
146        // to match the graphic coordinate system in android, we flip it vertically.
147        if (mTargetTexture == null) {
148            Matrix.translateM(matrix, 0, 0, height, 0);
149            Matrix.scaleM(matrix, 0, 1, -1, 1);
150        }
151    }
152
153    @Override
154    public void setAlpha(float alpha) {
155        Utils.assertTrue(alpha >= 0 && alpha <= 1);
156        mAlpha = alpha;
157    }
158
159    @Override
160    public float getAlpha() {
161        return mAlpha;
162    }
163
164    @Override
165    public void multiplyAlpha(float alpha) {
166        Utils.assertTrue(alpha >= 0 && alpha <= 1);
167        mAlpha *= alpha;
168    }
169
170    private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
171        return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
172    }
173
174    @Override
175    public void drawRect(float x, float y, float width, float height, GLPaint paint) {
176        GL11 gl = mGL;
177
178        mGLState.setColorMode(paint.getColor(), mAlpha);
179        mGLState.setLineWidth(paint.getLineWidth());
180
181        saveTransform();
182        translate(x, y);
183        scale(width, height, 1);
184
185        gl.glLoadMatrixf(mMatrixValues, 0);
186        gl.glDrawArrays(GL11.GL_LINE_LOOP, OFFSET_DRAW_RECT, 4);
187
188        restoreTransform();
189        mCountDrawLine++;
190    }
191
192    @Override
193    public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
194        GL11 gl = mGL;
195
196        mGLState.setColorMode(paint.getColor(), mAlpha);
197        mGLState.setLineWidth(paint.getLineWidth());
198
199        saveTransform();
200        translate(x1, y1);
201        scale(x2 - x1, y2 - y1, 1);
202
203        gl.glLoadMatrixf(mMatrixValues, 0);
204        gl.glDrawArrays(GL11.GL_LINE_STRIP, OFFSET_DRAW_LINE, 2);
205
206        restoreTransform();
207        mCountDrawLine++;
208    }
209
210    @Override
211    public void fillRect(float x, float y, float width, float height, int color) {
212        mGLState.setColorMode(color, mAlpha);
213        GL11 gl = mGL;
214
215        saveTransform();
216        translate(x, y);
217        scale(width, height, 1);
218
219        gl.glLoadMatrixf(mMatrixValues, 0);
220        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
221
222        restoreTransform();
223        mCountFillRect++;
224    }
225
226    @Override
227    public void translate(float x, float y, float z) {
228        Matrix.translateM(mMatrixValues, 0, x, y, z);
229    }
230
231    // This is a faster version of translate(x, y, z) because
232    // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
233    // (3) we unroll the loop
234    @Override
235    public void translate(float x, float y) {
236        float[] m = mMatrixValues;
237        m[12] += m[0] * x + m[4] * y;
238        m[13] += m[1] * x + m[5] * y;
239        m[14] += m[2] * x + m[6] * y;
240        m[15] += m[3] * x + m[7] * y;
241    }
242
243    @Override
244    public void scale(float sx, float sy, float sz) {
245        Matrix.scaleM(mMatrixValues, 0, sx, sy, sz);
246    }
247
248    @Override
249    public void rotate(float angle, float x, float y, float z) {
250        if (angle == 0) return;
251        float[] temp = mTempMatrix;
252        Matrix.setRotateM(temp, 0, angle, x, y, z);
253        Matrix.multiplyMM(temp, 16, mMatrixValues, 0, temp, 0);
254        System.arraycopy(temp, 16, mMatrixValues, 0, 16);
255    }
256
257    @Override
258    public void multiplyMatrix(float matrix[], int offset) {
259        float[] temp = mTempMatrix;
260        Matrix.multiplyMM(temp, 0, mMatrixValues, 0, matrix, offset);
261        System.arraycopy(temp, 0, mMatrixValues, 0, 16);
262    }
263
264    private void textureRect(float x, float y, float width, float height) {
265        GL11 gl = mGL;
266
267        saveTransform();
268        translate(x, y);
269        scale(width, height, 1);
270
271        gl.glLoadMatrixf(mMatrixValues, 0);
272        gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, 4);
273
274        restoreTransform();
275        mCountTextureRect++;
276    }
277
278    @Override
279    public void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
280            int uvBuffer, int indexBuffer, int indexCount) {
281        float alpha = mAlpha;
282        if (!bindTexture(tex)) return;
283
284        mGLState.setBlendEnabled(mBlendEnabled
285                && (!tex.isOpaque() || alpha < OPAQUE_ALPHA));
286        mGLState.setTextureAlpha(alpha);
287
288        // Reset the texture matrix. We will set our own texture coordinates
289        // below.
290        setTextureCoords(0, 0, 1, 1);
291
292        saveTransform();
293        translate(x, y);
294
295        mGL.glLoadMatrixf(mMatrixValues, 0);
296
297        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, xyBuffer);
298        mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
299
300        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, uvBuffer);
301        mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
302
303        mGL.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
304        mGL.glDrawElements(GL11.GL_TRIANGLE_STRIP,
305                indexCount, GL11.GL_UNSIGNED_BYTE, 0);
306
307        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBoxCoords);
308        mGL.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
309        mGL.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
310
311        restoreTransform();
312        mCountDrawMesh++;
313    }
314
315    // Transforms two points by the given matrix m. The result
316    // {x1', y1', x2', y2'} are stored in mMapPointsBuffer and also returned.
317    private float[] mapPoints(float m[], int x1, int y1, int x2, int y2) {
318        float[] r = mMapPointsBuffer;
319
320        // Multiply m and (x1 y1 0 1) to produce (x3 y3 z3 w3). z3 is unused.
321        float x3 = m[0] * x1 + m[4] * y1 + m[12];
322        float y3 = m[1] * x1 + m[5] * y1 + m[13];
323        float w3 = m[3] * x1 + m[7] * y1 + m[15];
324        r[0] = x3 / w3;
325        r[1] = y3 / w3;
326
327        // Same for x2 y2.
328        float x4 = m[0] * x2 + m[4] * y2 + m[12];
329        float y4 = m[1] * x2 + m[5] * y2 + m[13];
330        float w4 = m[3] * x2 + m[7] * y2 + m[15];
331        r[2] = x4 / w4;
332        r[3] = y4 / w4;
333
334        return r;
335    }
336
337    private void drawBoundTexture(
338            BasicTexture texture, int x, int y, int width, int height) {
339        // Test whether it has been rotated or flipped, if so, glDrawTexiOES
340        // won't work
341        if (isMatrixRotatedOrFlipped(mMatrixValues)) {
342            if (texture.hasBorder()) {
343                setTextureCoords(
344                        1.0f / texture.getTextureWidth(),
345                        1.0f / texture.getTextureHeight(),
346                        (texture.getWidth() - 1.0f) / texture.getTextureWidth(),
347                        (texture.getHeight() - 1.0f) / texture.getTextureHeight());
348            } else {
349                setTextureCoords(0, 0,
350                        (float) texture.getWidth() / texture.getTextureWidth(),
351                        (float) texture.getHeight() / texture.getTextureHeight());
352            }
353            textureRect(x, y, width, height);
354        } else {
355            // draw the rect from bottom-left to top-right
356            float points[] = mapPoints(
357                    mMatrixValues, x, y + height, x + width, y);
358            x = (int) (points[0] + 0.5f);
359            y = (int) (points[1] + 0.5f);
360            width = (int) (points[2] + 0.5f) - x;
361            height = (int) (points[3] + 0.5f) - y;
362            if (width > 0 && height > 0) {
363                ((GL11Ext) mGL).glDrawTexiOES(x, y, 0, width, height);
364                mCountTextureOES++;
365            }
366        }
367    }
368
369    @Override
370    public void drawTexture(
371            BasicTexture texture, int x, int y, int width, int height) {
372        drawTexture(texture, x, y, width, height, mAlpha);
373    }
374
375    private void drawTexture(BasicTexture texture,
376            int x, int y, int width, int height, float alpha) {
377        if (width <= 0 || height <= 0) return;
378
379        mGLState.setBlendEnabled(mBlendEnabled
380                && (!texture.isOpaque() || alpha < OPAQUE_ALPHA));
381        if (!bindTexture(texture)) return;
382        mGLState.setTextureAlpha(alpha);
383        drawBoundTexture(texture, x, y, width, height);
384    }
385
386    @Override
387    public void drawTexture(BasicTexture texture, RectF source, RectF target) {
388        if (target.width() <= 0 || target.height() <= 0) return;
389
390        // Copy the input to avoid changing it.
391        mDrawTextureSourceRect.set(source);
392        mDrawTextureTargetRect.set(target);
393        source = mDrawTextureSourceRect;
394        target = mDrawTextureTargetRect;
395
396        mGLState.setBlendEnabled(mBlendEnabled
397                && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
398        if (!bindTexture(texture)) return;
399        convertCoordinate(source, target, texture);
400        setTextureCoords(source);
401        mGLState.setTextureAlpha(mAlpha);
402        textureRect(target.left, target.top, target.width(), target.height());
403    }
404
405    @Override
406    public void drawTexture(BasicTexture texture, float[] mTextureTransform,
407            int x, int y, int w, int h) {
408        mGLState.setBlendEnabled(mBlendEnabled
409                && (!texture.isOpaque() || mAlpha < OPAQUE_ALPHA));
410        if (!bindTexture(texture)) return;
411        setTextureCoords(mTextureTransform);
412        mGLState.setTextureAlpha(mAlpha);
413        textureRect(x, y, w, h);
414    }
415
416    // This function changes the source coordinate to the texture coordinates.
417    // It also clips the source and target coordinates if it is beyond the
418    // bound of the texture.
419    private static void convertCoordinate(RectF source, RectF target,
420            BasicTexture texture) {
421
422        int width = texture.getWidth();
423        int height = texture.getHeight();
424        int texWidth = texture.getTextureWidth();
425        int texHeight = texture.getTextureHeight();
426        // Convert to texture coordinates
427        source.left /= texWidth;
428        source.right /= texWidth;
429        source.top /= texHeight;
430        source.bottom /= texHeight;
431
432        // Clip if the rendering range is beyond the bound of the texture.
433        float xBound = (float) width / texWidth;
434        if (source.right > xBound) {
435            target.right = target.left + target.width() *
436                    (xBound - source.left) / source.width();
437            source.right = xBound;
438        }
439        float yBound = (float) height / texHeight;
440        if (source.bottom > yBound) {
441            target.bottom = target.top + target.height() *
442                    (yBound - source.top) / source.height();
443            source.bottom = yBound;
444        }
445    }
446
447    @Override
448    public void drawMixed(BasicTexture from,
449            int toColor, float ratio, int x, int y, int w, int h) {
450        drawMixed(from, toColor, ratio, x, y, w, h, mAlpha);
451    }
452
453    private boolean bindTexture(BasicTexture texture) {
454        if (!texture.onBind(this)) return false;
455        int target = texture.getTarget();
456        mGLState.setTextureTarget(target);
457        mGL.glBindTexture(target, texture.getId());
458        return true;
459    }
460
461    private void setTextureColor(float r, float g, float b, float alpha) {
462        float[] color = mTextureColor;
463        color[0] = r;
464        color[1] = g;
465        color[2] = b;
466        color[3] = alpha;
467    }
468
469    private void setMixedColor(int toColor, float ratio, float alpha) {
470        //
471        // The formula we want:
472        //     alpha * ((1 - ratio) * from + ratio * to)
473        //
474        // The formula that GL supports is in the form of:
475        //     combo * from + (1 - combo) * to * scale
476        //
477        // So, we have combo = alpha * (1 - ratio)
478        //     and     scale = alpha * ratio / (1 - combo)
479        //
480        float combo = alpha * (1 - ratio);
481        float scale = alpha * ratio / (1 - combo);
482
483        // Specify the interpolation factor via the alpha component of
484        // GL_TEXTURE_ENV_COLORs.
485        // RGB component are get from toColor and will used as SRC1
486        float colorScale = scale * (toColor >>> 24) / (0xff * 0xff);
487        setTextureColor(((toColor >>> 16) & 0xff) * colorScale,
488                ((toColor >>> 8) & 0xff) * colorScale,
489                (toColor & 0xff) * colorScale, combo);
490        GL11 gl = mGL;
491        gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, mTextureColor, 0);
492
493        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
494        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
495        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_RGB, GL11.GL_CONSTANT);
496        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_RGB, GL11.GL_SRC_COLOR);
497        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC1_ALPHA, GL11.GL_CONSTANT);
498        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND1_ALPHA, GL11.GL_SRC_ALPHA);
499
500        // Wire up the interpolation factor for RGB.
501        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
502        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
503
504        // Wire up the interpolation factor for alpha.
505        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
506        gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
507
508    }
509
510    @Override
511    public void drawMixed(BasicTexture from, int toColor, float ratio,
512            RectF source, RectF target) {
513        if (target.width() <= 0 || target.height() <= 0) return;
514
515        if (ratio <= 0.01f) {
516            drawTexture(from, source, target);
517            return;
518        } else if (ratio >= 1) {
519            fillRect(target.left, target.top, target.width(), target.height(), toColor);
520            return;
521        }
522
523        float alpha = mAlpha;
524
525        // Copy the input to avoid changing it.
526        mDrawTextureSourceRect.set(source);
527        mDrawTextureTargetRect.set(target);
528        source = mDrawTextureSourceRect;
529        target = mDrawTextureTargetRect;
530
531        mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
532                || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
533
534        if (!bindTexture(from)) return;
535
536        // Interpolate the RGB and alpha values between both textures.
537        mGLState.setTexEnvMode(GL11.GL_COMBINE);
538        setMixedColor(toColor, ratio, alpha);
539        convertCoordinate(source, target, from);
540        setTextureCoords(source);
541        textureRect(target.left, target.top, target.width(), target.height());
542        mGLState.setTexEnvMode(GL11.GL_REPLACE);
543    }
544
545    private void drawMixed(BasicTexture from, int toColor,
546            float ratio, int x, int y, int width, int height, float alpha) {
547        // change from 0 to 0.01f to prevent getting divided by zero below
548        if (ratio <= 0.01f) {
549            drawTexture(from, x, y, width, height, alpha);
550            return;
551        } else if (ratio >= 1) {
552            fillRect(x, y, width, height, toColor);
553            return;
554        }
555
556        mGLState.setBlendEnabled(mBlendEnabled && (!from.isOpaque()
557                || !Utils.isOpaque(toColor) || alpha < OPAQUE_ALPHA));
558
559        final GL11 gl = mGL;
560        if (!bindTexture(from)) return;
561
562        // Interpolate the RGB and alpha values between both textures.
563        mGLState.setTexEnvMode(GL11.GL_COMBINE);
564        setMixedColor(toColor, ratio, alpha);
565
566        drawBoundTexture(from, x, y, width, height);
567        mGLState.setTexEnvMode(GL11.GL_REPLACE);
568    }
569
570    // TODO: the code only work for 2D should get fixed for 3D or removed
571    private static final int MSKEW_X = 4;
572    private static final int MSKEW_Y = 1;
573    private static final int MSCALE_X = 0;
574    private static final int MSCALE_Y = 5;
575
576    private static boolean isMatrixRotatedOrFlipped(float matrix[]) {
577        final float eps = 1e-5f;
578        return Math.abs(matrix[MSKEW_X]) > eps
579                || Math.abs(matrix[MSKEW_Y]) > eps
580                || matrix[MSCALE_X] < -eps
581                || matrix[MSCALE_Y] > eps;
582    }
583
584    private static class GLState {
585
586        private final GL11 mGL;
587
588        private int mTexEnvMode = GL11.GL_REPLACE;
589        private float mTextureAlpha = 1.0f;
590        private int mTextureTarget = GL11.GL_TEXTURE_2D;
591        private boolean mBlendEnabled = true;
592        private float mLineWidth = 1.0f;
593        private boolean mLineSmooth = false;
594
595        public GLState(GL11 gl) {
596            mGL = gl;
597
598            // Disable unused state
599            gl.glDisable(GL11.GL_LIGHTING);
600
601            // Enable used features
602            gl.glEnable(GL11.GL_DITHER);
603
604            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
605            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
606            gl.glEnable(GL11.GL_TEXTURE_2D);
607
608            gl.glTexEnvf(GL11.GL_TEXTURE_ENV,
609                    GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
610
611            // Set the background color
612            gl.glClearColor(0f, 0f, 0f, 0f);
613
614            gl.glEnable(GL11.GL_BLEND);
615            gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
616
617            // We use 565 or 8888 format, so set the alignment to 2 bytes/pixel.
618            gl.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
619        }
620
621        public void setTexEnvMode(int mode) {
622            if (mTexEnvMode == mode) return;
623            mTexEnvMode = mode;
624            mGL.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, mode);
625        }
626
627        public void setLineWidth(float width) {
628            if (mLineWidth == width) return;
629            mLineWidth = width;
630            mGL.glLineWidth(width);
631        }
632
633        public void setTextureAlpha(float alpha) {
634            if (mTextureAlpha == alpha) return;
635            mTextureAlpha = alpha;
636            if (alpha >= OPAQUE_ALPHA) {
637                // The alpha is need for those texture without alpha channel
638                mGL.glColor4f(1, 1, 1, 1);
639                setTexEnvMode(GL11.GL_REPLACE);
640            } else {
641                mGL.glColor4f(alpha, alpha, alpha, alpha);
642                setTexEnvMode(GL11.GL_MODULATE);
643            }
644        }
645
646        public void setColorMode(int color, float alpha) {
647            setBlendEnabled(!Utils.isOpaque(color) || alpha < OPAQUE_ALPHA);
648
649            // Set mTextureAlpha to an invalid value, so that it will reset
650            // again in setTextureAlpha(float) later.
651            mTextureAlpha = -1.0f;
652
653            setTextureTarget(0);
654
655            float prealpha = (color >>> 24) * alpha * 65535f / 255f / 255f;
656            mGL.glColor4x(
657                    Math.round(((color >> 16) & 0xFF) * prealpha),
658                    Math.round(((color >> 8) & 0xFF) * prealpha),
659                    Math.round((color & 0xFF) * prealpha),
660                    Math.round(255 * prealpha));
661        }
662
663        // target is a value like GL_TEXTURE_2D. If target = 0, texturing is disabled.
664        public void setTextureTarget(int target) {
665            if (mTextureTarget == target) return;
666            if (mTextureTarget != 0) {
667                mGL.glDisable(mTextureTarget);
668            }
669            mTextureTarget = target;
670            if (mTextureTarget != 0) {
671                mGL.glEnable(mTextureTarget);
672            }
673        }
674
675        public void setBlendEnabled(boolean enabled) {
676            if (mBlendEnabled == enabled) return;
677            mBlendEnabled = enabled;
678            if (enabled) {
679                mGL.glEnable(GL11.GL_BLEND);
680            } else {
681                mGL.glDisable(GL11.GL_BLEND);
682            }
683        }
684    }
685
686    @Override
687    public void clearBuffer(float[] argb) {
688        if(argb != null && argb.length == 4) {
689            mGL.glClearColor(argb[1], argb[2], argb[3], argb[0]);
690        } else {
691            mGL.glClearColor(0, 0, 0, 1);
692        }
693        mGL.glClear(GL10.GL_COLOR_BUFFER_BIT);
694    }
695
696    @Override
697    public void clearBuffer() {
698        clearBuffer(null);
699    }
700
701    private void setTextureCoords(RectF source) {
702        setTextureCoords(source.left, source.top, source.right, source.bottom);
703    }
704
705    private void setTextureCoords(float left, float top,
706            float right, float bottom) {
707        mGL.glMatrixMode(GL11.GL_TEXTURE);
708        mTextureMatrixValues[0] = right - left;
709        mTextureMatrixValues[5] = bottom - top;
710        mTextureMatrixValues[10] = 1;
711        mTextureMatrixValues[12] = left;
712        mTextureMatrixValues[13] = top;
713        mTextureMatrixValues[15] = 1;
714        mGL.glLoadMatrixf(mTextureMatrixValues, 0);
715        mGL.glMatrixMode(GL11.GL_MODELVIEW);
716    }
717
718    private void setTextureCoords(float[] mTextureTransform) {
719        mGL.glMatrixMode(GL11.GL_TEXTURE);
720        mGL.glLoadMatrixf(mTextureTransform, 0);
721        mGL.glMatrixMode(GL11.GL_MODELVIEW);
722    }
723
724    // unloadTexture and deleteBuffer can be called from the finalizer thread,
725    // so we synchronized on the mUnboundTextures object.
726    @Override
727    public boolean unloadTexture(BasicTexture t) {
728        synchronized (mUnboundTextures) {
729            if (!t.isLoaded()) return false;
730            mUnboundTextures.add(t.mId);
731            return true;
732        }
733    }
734
735    @Override
736    public void deleteBuffer(int bufferId) {
737        synchronized (mUnboundTextures) {
738            mDeleteBuffers.add(bufferId);
739        }
740    }
741
742    @Override
743    public void deleteRecycledResources() {
744        synchronized (mUnboundTextures) {
745            IntArray ids = mUnboundTextures;
746            if (ids.size() > 0) {
747                mGLId.glDeleteTextures(mGL, ids.size(), ids.getInternalArray(), 0);
748                ids.clear();
749            }
750
751            ids = mDeleteBuffers;
752            if (ids.size() > 0) {
753                mGLId.glDeleteBuffers(mGL, ids.size(), ids.getInternalArray(), 0);
754                ids.clear();
755            }
756        }
757    }
758
759    @Override
760    public void save() {
761        save(SAVE_FLAG_ALL);
762    }
763
764    @Override
765    public void save(int saveFlags) {
766        ConfigState config = obtainRestoreConfig();
767
768        if ((saveFlags & SAVE_FLAG_ALPHA) != 0) {
769            config.mAlpha = mAlpha;
770        } else {
771            config.mAlpha = -1;
772        }
773
774        if ((saveFlags & SAVE_FLAG_MATRIX) != 0) {
775            System.arraycopy(mMatrixValues, 0, config.mMatrix, 0, 16);
776        } else {
777            config.mMatrix[0] = Float.NEGATIVE_INFINITY;
778        }
779
780        mRestoreStack.add(config);
781    }
782
783    @Override
784    public void restore() {
785        if (mRestoreStack.isEmpty()) throw new IllegalStateException();
786        ConfigState config = mRestoreStack.remove(mRestoreStack.size() - 1);
787        config.restore(this);
788        freeRestoreConfig(config);
789    }
790
791    private void freeRestoreConfig(ConfigState action) {
792        action.mNextFree = mRecycledRestoreAction;
793        mRecycledRestoreAction = action;
794    }
795
796    private ConfigState obtainRestoreConfig() {
797        if (mRecycledRestoreAction != null) {
798            ConfigState result = mRecycledRestoreAction;
799            mRecycledRestoreAction = result.mNextFree;
800            return result;
801        }
802        return new ConfigState();
803    }
804
805    private static class ConfigState {
806        float mAlpha;
807        float mMatrix[] = new float[16];
808        ConfigState mNextFree;
809
810        public void restore(GLES11Canvas canvas) {
811            if (mAlpha >= 0) canvas.setAlpha(mAlpha);
812            if (mMatrix[0] != Float.NEGATIVE_INFINITY) {
813                System.arraycopy(mMatrix, 0, canvas.mMatrixValues, 0, 16);
814            }
815        }
816    }
817
818    @Override
819    public void dumpStatisticsAndClear() {
820        String line = String.format(
821                "MESH:%d, TEX_OES:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d",
822                mCountDrawMesh, mCountTextureRect, mCountTextureOES,
823                mCountFillRect, mCountDrawLine);
824        mCountDrawMesh = 0;
825        mCountTextureRect = 0;
826        mCountTextureOES = 0;
827        mCountFillRect = 0;
828        mCountDrawLine = 0;
829        Log.d(TAG, line);
830    }
831
832    private void saveTransform() {
833        System.arraycopy(mMatrixValues, 0, mTempMatrix, 0, 16);
834    }
835
836    private void restoreTransform() {
837        System.arraycopy(mTempMatrix, 0, mMatrixValues, 0, 16);
838    }
839
840    private void setRenderTarget(RawTexture texture) {
841        GL11ExtensionPack gl11ep = (GL11ExtensionPack) mGL;
842
843        if (mTargetTexture == null && texture != null) {
844            mGLId.glGenBuffers(1, mFrameBuffer, 0);
845            gl11ep.glBindFramebufferOES(
846                    GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFrameBuffer[0]);
847        }
848        if (mTargetTexture != null && texture  == null) {
849            gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
850            gl11ep.glDeleteFramebuffersOES(1, mFrameBuffer, 0);
851        }
852
853        mTargetTexture = texture;
854        if (texture == null) {
855            setSize(mScreenWidth, mScreenHeight);
856        } else {
857            setSize(texture.getWidth(), texture.getHeight());
858
859            if (!texture.isLoaded()) texture.prepare(this);
860
861            gl11ep.glFramebufferTexture2DOES(
862                    GL11ExtensionPack.GL_FRAMEBUFFER_OES,
863                    GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES,
864                    GL11.GL_TEXTURE_2D, texture.getId(), 0);
865
866            checkFramebufferStatus(gl11ep);
867        }
868    }
869
870    @Override
871    public void endRenderTarget() {
872        RawTexture texture = mTargetStack.remove(mTargetStack.size() - 1);
873        setRenderTarget(texture);
874        restore(); // restore matrix and alpha
875    }
876
877    @Override
878    public void beginRenderTarget(RawTexture texture) {
879        save(); // save matrix and alpha
880        mTargetStack.add(mTargetTexture);
881        setRenderTarget(texture);
882    }
883
884    private static void checkFramebufferStatus(GL11ExtensionPack gl11ep) {
885        int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
886        if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) {
887            String msg = "";
888            switch (status) {
889                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES:
890                    msg = "FRAMEBUFFER_FORMATS";
891                    break;
892                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES:
893                    msg = "FRAMEBUFFER_ATTACHMENT";
894                    break;
895                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES:
896                    msg = "FRAMEBUFFER_MISSING_ATTACHMENT";
897                    break;
898                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES:
899                    msg = "FRAMEBUFFER_DRAW_BUFFER";
900                    break;
901                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES:
902                    msg = "FRAMEBUFFER_READ_BUFFER";
903                    break;
904                case GL11ExtensionPack.GL_FRAMEBUFFER_UNSUPPORTED_OES:
905                    msg = "FRAMEBUFFER_UNSUPPORTED";
906                    break;
907                case GL11ExtensionPack.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES:
908                    msg = "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
909                    break;
910            }
911            throw new RuntimeException(msg + ":" + Integer.toHexString(status));
912        }
913    }
914
915    @Override
916    public void setTextureParameters(BasicTexture texture) {
917        int width = texture.getWidth();
918        int height = texture.getHeight();
919        // Define a vertically flipped crop rectangle for OES_draw_texture.
920        // The four values in sCropRect are: left, bottom, width, and
921        // height. Negative value of width or height means flip.
922        sCropRect[0] = 0;
923        sCropRect[1] = height;
924        sCropRect[2] = width;
925        sCropRect[3] = -height;
926
927        // Set texture parameters.
928        int target = texture.getTarget();
929        mGL.glBindTexture(target, texture.getId());
930        mGL.glTexParameterfv(target, GL11Ext.GL_TEXTURE_CROP_RECT_OES, sCropRect, 0);
931        mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
932        mGL.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
933        mGL.glTexParameterf(target, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
934        mGL.glTexParameterf(target, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
935    }
936
937    @Override
938    public void initializeTextureSize(BasicTexture texture, int format, int type) {
939        int target = texture.getTarget();
940        mGL.glBindTexture(target, texture.getId());
941        int width = texture.getTextureWidth();
942        int height = texture.getTextureHeight();
943        mGL.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
944    }
945
946    @Override
947    public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
948        int target = texture.getTarget();
949        mGL.glBindTexture(target, texture.getId());
950        GLUtils.texImage2D(target, 0, bitmap, 0);
951    }
952
953    @Override
954    public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
955            int format, int type) {
956        int target = texture.getTarget();
957        mGL.glBindTexture(target, texture.getId());
958        GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
959    }
960
961    @Override
962    public int uploadBuffer(FloatBuffer buf) {
963        return uploadBuffer(buf, Float.SIZE / Byte.SIZE);
964    }
965
966    @Override
967    public int uploadBuffer(ByteBuffer buf) {
968        return uploadBuffer(buf, 1);
969    }
970
971    private int uploadBuffer(Buffer buf, int elementSize) {
972        int[] bufferIds = new int[1];
973        mGLId.glGenBuffers(bufferIds.length, bufferIds, 0);
974        int bufferId = bufferIds[0];
975        mGL.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferId);
976        mGL.glBufferData(GL11.GL_ARRAY_BUFFER, buf.capacity() * elementSize, buf,
977                GL11.GL_STATIC_DRAW);
978        return bufferId;
979    }
980
981    @Override
982    public void recoverFromLightCycle() {
983        // This is only required for GLES20
984    }
985
986    @Override
987    public void getBounds(Rect bounds, int x, int y, int width, int height) {
988        // This is only required for GLES20
989    }
990
991    @Override
992    public GLId getGLId() {
993        return mGLId;
994    }
995}
996