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