ImageWallpaper.java revision a8e5a2bcd6a0d35893187c6df42425c03be005da
1bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker/*
256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * Copyright (C) 2009 The Android Open Source Project
356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker *
456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * Licensed under the Apache License, Version 2.0 (the "License");
556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * you may not use this file except in compliance with the License.
656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * You may obtain a copy of the License at
756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker *
856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker *      http://www.apache.org/licenses/LICENSE-2.0
956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker *
1056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * Unless required by applicable law or agreed to in writing, software
1156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * distributed under the License is distributed on an "AS IS" BASIS,
1256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * See the License for the specific language governing permissions and
1456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * limitations under the License.
1556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker */
1656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
1756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerpackage com.android.systemui;
1856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
1956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.app.ActivityManager;
2056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.app.WallpaperManager;
2156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.content.BroadcastReceiver;
2256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.content.Context;
2356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.content.Intent;
2456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.graphics.Bitmap;
257eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinkerimport android.graphics.Canvas;
2656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.graphics.Rect;
2756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.graphics.Region.Op;
2856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.opengl.GLUtils;
2956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.os.SystemProperties;
3056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.renderscript.Matrix4f;
3156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.service.wallpaper.WallpaperService;
3256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.util.Log;
3356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.view.Display;
3456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.view.MotionEvent;
3556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.view.SurfaceHolder;
3656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport android.view.WindowManager;
3756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
387eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinkerimport javax.microedition.khronos.egl.EGL10;
397eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinkerimport javax.microedition.khronos.egl.EGLConfig;
4056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport javax.microedition.khronos.egl.EGLContext;
4156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport javax.microedition.khronos.egl.EGLDisplay;
4256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport javax.microedition.khronos.egl.EGLSurface;
4356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport javax.microedition.khronos.opengles.GL;
4456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport java.io.IOException;
4556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport java.nio.ByteBuffer;
4656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport java.nio.ByteOrder;
4756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport java.nio.FloatBuffer;
4856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
4956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport static android.opengl.GLES20.*;
5056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerimport static javax.microedition.khronos.egl.EGL10.*;
5156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
5256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker/**
5356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker * Default built-in wallpaper that simply shows a static image.
5456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker */
5556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker@SuppressWarnings({"UnusedDeclaration"})
5656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinkerpublic class ImageWallpaper extends WallpaperService {
5756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    private static final String TAG = "ImageWallpaper";
5856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    private static final String GL_LOG_TAG = "ImageWallpaperGL";
5956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    private static final boolean DEBUG = false;
6056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
6156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
6256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    static final boolean FIXED_SIZED_SURFACE = true;
63611d3d481605049c8ca77f27131282993f448664Jeff Tinker    static final boolean USE_OPENGL = true;
64611d3d481605049c8ca77f27131282993f448664Jeff Tinker
65611d3d481605049c8ca77f27131282993f448664Jeff Tinker    WallpaperManager mWallpaperManager;
66611d3d481605049c8ca77f27131282993f448664Jeff Tinker
67611d3d481605049c8ca77f27131282993f448664Jeff Tinker    boolean mIsHwAccelerated;
6856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
6956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    @Override
7056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    public void onCreate() {
7156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        super.onCreate();
7256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
7356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
7456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        //noinspection PointlessBooleanExpression,ConstantConditions
7556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        if (FIXED_SIZED_SURFACE && USE_OPENGL) {
7656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (!isEmulator()) {
7756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                WindowManager windowManager =
7856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                        (WindowManager) getSystemService(Context.WINDOW_SERVICE);
7956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                Display display = windowManager.getDefaultDisplay();
807eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                mIsHwAccelerated = ActivityManager.isHighEndGfx(display);
81bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
82bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        }
8346722ff44a979c4674c5668189f631d8f77929fdRonghua Wu    }
849a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
859a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker    private static boolean isEmulator() {
869a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
8756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker    }
8856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
89bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker    public Engine onCreateEngine() {
90bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        return new DrawableEngine();
91b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker    }
92b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
93bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker    class DrawableEngine extends Engine {
94bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
95b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        static final int EGL_OPENGL_ES2_BIT = 4;
96b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
9756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        private final Object mLock = new Object[0];
9856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
9906a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        // TODO: Not currently used, keeping around until we know we don't need it
10006a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        @SuppressWarnings({"UnusedDeclaration"})
10106a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        private WallpaperObserver mReceiver;
10206a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker
10306a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        Bitmap mBackground;
10406a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        int mBackgroundWidth = -1, mBackgroundHeight = -1;
10506a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        float mXOffset;
10606a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker        float mYOffset;
10706a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker
1089a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        boolean mVisible = true;
1099a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        boolean mRedrawNeeded;
1109a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        boolean mOffsetsChanged;
1119a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        int mLastXTranslation;
1129a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        int mLastYTranslation;
1139a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
1149a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private EGL10 mEgl;
1159a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private EGLDisplay mEglDisplay;
1169a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private EGLConfig mEglConfig;
1179a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private EGLContext mEglContext;
1189a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private EGLSurface mEglSurface;
1199a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private GL mGL;
1209a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
1219a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private static final String sSimpleVS =
1229a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                "attribute vec4 position;\n" +
1239a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                "attribute vec2 texCoords;\n" +
1249a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                "varying vec2 outTexCoords;\n" +
1259a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                "uniform mat4 projection;\n" +
12656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "\nvoid main(void) {\n" +
12756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "    outTexCoords = texCoords;\n" +
12856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "    gl_Position = projection * position;\n" +
12956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "}\n\n";
13056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        private static final String sSimpleFS =
13156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "precision mediump float;\n\n" +
13256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "varying vec2 outTexCoords;\n" +
13356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "uniform sampler2D texture;\n" +
13456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "\nvoid main(void) {\n" +
13556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
136bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                "}\n\n";
137bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
138bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        private static final int FLOAT_SIZE_BYTES = 4;
13956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
14056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
141b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
142b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
143b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        class WallpaperObserver extends BroadcastReceiver {
144b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            public void onReceive(Context context, Intent intent) {
145b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                if (DEBUG) {
146b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                    Log.d(TAG, "onReceive");
147b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                }
148bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
14956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                synchronized (mLock) {
150bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                    mBackgroundWidth = mBackgroundHeight = -1;
15156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    mBackground = null;
152b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                    mRedrawNeeded = true;
153b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                    drawFrameLocked();
154b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                }
155b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            }
15656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        }
157b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
15856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        @Override
159bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        public void onCreate(SurfaceHolder surfaceHolder) {
160bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (DEBUG) {
16156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                Log.d(TAG, "onCreate");
162bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
16356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
164b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            super.onCreate(surfaceHolder);
165bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
166bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            // TODO: Don't need this currently because the wallpaper service
167bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            // will restart the image wallpaper whenever the image changes.
16806a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker            //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
16906a8cd6d19a20843d137f7e65f4c81da685c291eJeff Tinker            //mReceiver = new WallpaperObserver();
17056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            //registerReceiver(mReceiver, filter, null, mHandler);
171b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
172bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            updateSurfaceSize(surfaceHolder);
173b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
174b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            setOffsetNotificationsEnabled(false);
175b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        }
176b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
177b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        @Override
178b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        public void onDestroy() {
179b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            super.onDestroy();
180b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            if (mReceiver != null) {
181b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                unregisterReceiver(mReceiver);
182b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            }
183b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        }
184b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker
185b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker        @Override
186bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
187bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            super.onDesiredSizeChanged(desiredWidth, desiredHeight);
18856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            SurfaceHolder surfaceHolder = getSurfaceHolder();
189b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker            if (surfaceHolder != null) {
190b84d1cad1d481f75aa03d55ac07e4ff58dbfb84aJeff Tinker                updateSurfaceSize(surfaceHolder);
191bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
192bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        }
193bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
194bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        void updateSurfaceSize(SurfaceHolder surfaceHolder) {
195bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (FIXED_SIZED_SURFACE) {
19656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                // Used a fixed size surface, because we are special.  We can do
19756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                // this because we know the current design of window animations doesn't
19856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                // cause this to break.
19956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
20056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            } else {
20156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                surfaceHolder.setSizeFromLayout();
20256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
203bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        }
204bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
20556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        @Override
20656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        public void onVisibilityChanged(boolean visible) {
20756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (DEBUG) {
208bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                Log.d(TAG, "onVisibilityChanged: visible=" + visible);
20956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
21056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
21156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            synchronized (mLock) {
212c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                if (mVisible != visible) {
213c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                    if (DEBUG) {
214c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                        Log.d(TAG, "Visibility changed to visible=" + visible);
21556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    }
21656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    mVisible = visible;
21756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    drawFrameLocked();
21856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                }
219c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            }
220c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker        }
221c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker
22256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        @Override
223bc6b9e7f8a6b991263906c1b0bd1db0e45b59282Jeff Tinker        public void onTouchEvent(MotionEvent event) {
224bc6b9e7f8a6b991263906c1b0bd1db0e45b59282Jeff Tinker            super.onTouchEvent(event);
225bc6b9e7f8a6b991263906c1b0bd1db0e45b59282Jeff Tinker        }
22656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
22756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        @Override
22856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        public void onOffsetsChanged(float xOffset, float yOffset,
22956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                float xOffsetStep, float yOffsetStep,
23056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                int xPixels, int yPixels) {
23156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (DEBUG) {
23256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
23356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
23456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
23556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
23656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
23756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            synchronized (mLock) {
23856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                if (mXOffset != xOffset || mYOffset != yOffset) {
23956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    if (DEBUG) {
24056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                        Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
24156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    }
24256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    mXOffset = xOffset;
24357481590b98518865e266004e7ca4737abd5508dJeff Tinker                    mYOffset = yOffset;
24456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    mOffsetsChanged = true;
24557481590b98518865e266004e7ca4737abd5508dJeff Tinker                }
24656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                drawFrameLocked();
24756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
24856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        }
24956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
25056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        @Override
25156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
25256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (DEBUG) {
25356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
25456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
25556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
25656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            super.onSurfaceChanged(holder, format, width, height);
25756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
25856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            synchronized (mLock) {
25956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                mRedrawNeeded = true;
26056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                drawFrameLocked();
26156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
26256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        }
26356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
26456c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker        void drawFrameLocked() {
26556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (!mVisible) {
26656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                if (DEBUG) {
26756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");
26856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                }
26956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                return;
27056c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            }
27156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (!mRedrawNeeded && !mOffsetsChanged) {
27256c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                if (DEBUG) {
27356c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
274bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                            + "and offsets have not changed.");
275bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                }
276bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                return;
277bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
278bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
279bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
280bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // If we don't yet know the size of the wallpaper bitmap,
281bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // we need to get it now.
282bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                updateWallpaperLocked();
283bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
284bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
285bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            SurfaceHolder sh = getSurfaceHolder();
286bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            final Rect frame = sh.getSurfaceFrame();
287bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            final int dw = frame.width();
288bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            final int dh = frame.height();
289bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            final int availw = dw - mBackgroundWidth;
290bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            final int availh = dh - mBackgroundHeight;
291bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
292bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
293bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
294bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            mOffsetsChanged = false;
295bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (!mRedrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
296bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                if (DEBUG) {
297bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                    Log.d(TAG, "Suppressed drawFrame since the image has not "
298bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                            + "actually moved an integral number of pixels.");
299bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                }
300bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                return;
301bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
302bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            mRedrawNeeded = false;
303bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            mLastXTranslation = xPixels;
304bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            mLastYTranslation = yPixels;
305bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
306bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (mBackground == null) {
307bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // If we somehow got to this point after we have last flushed
308bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // the wallpaper, well we really need it to draw again.  So
309bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // seems like we need to reload it.  Ouch.
310bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                updateWallpaperLocked();
311bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
312bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
313bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (mIsHwAccelerated) {
314bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
315bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
316bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                }
317bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            } else {
318bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
319bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
320bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
321bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            if (FIXED_SIZED_SURFACE) {
322bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // If the surface is fixed-size, we should only need to
323bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // draw it once and then we'll let the window manager
324bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // position it appropriately.  As such, we no longer needed
325bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                // the loaded bitmap.  Yay!
326bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                mBackground = null;
327bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker                mWallpaperManager.forgetLoadedWallpaper();
328bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
329bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        }
330bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker
331bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker        void updateWallpaperLocked() {
332bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            Throwable exception = null;
333c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            try {
334c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                mBackground = mWallpaperManager.getBitmap();
335c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            } catch (RuntimeException e) {
336c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                exception = e;
337c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            } catch (OutOfMemoryError e) {
338c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker                exception = e;
339c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            }
340c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker
341c2f10f20ec9be98f363d6739ba1552955efe6532Jeff Tinker            if (exception != null) {
3427eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                mBackground = null;
3437eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                // Note that if we do fail at this, and the default wallpaper can't
3447eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                // be loaded, we will go into a cycle.  Don't do a build where the
3457eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                // default wallpaper can't be loaded.
3467eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                Log.w(TAG, "Unable to load wallpaper!", exception);
3477eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                try {
3487eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    mWallpaperManager.clear();
3499a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                } catch (IOException ex) {
3507eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    // now we're really screwed.
3517eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
3527eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                }
353bcbd78bd246f1e68e44b4a6d6257dbfcec8e65b3Jeff Tinker            }
3549a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
3559a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0;
3569a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0;
3579a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        }
3589a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
3599a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker        private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
3609a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            Canvas c = sh.lockCanvas();
36156c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (c != null) {
3627eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                try {
3637eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    if (DEBUG) {
3647eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                        Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
36556c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    }
36656c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker
36756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                    c.translate(x, y);
3687eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    if (w < 0 || h < 0) {
3697eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                        c.save(Canvas.CLIP_SAVE_FLAG);
3707eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
3717eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                        c.drawColor(0xff000000);
3729a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                        c.restore();
3737eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker                    }
3749a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                    if (mBackground != null) {
3759a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                        c.drawBitmap(mBackground, 0, 0, null);
3769a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                    }
3779a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                } finally {
3789a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                    sh.unlockCanvasAndPost(c);
3799a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker                }
3809a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            }
3817eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker        }
3827eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker
3837eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker        private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
3847eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            if (!initGL(sh)) return false;
3857eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker
3867eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            final float right = left + mBackgroundWidth;
3877eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            final float bottom = top + mBackgroundHeight;
3887eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker
3897eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            final Rect frame = sh.getSurfaceFrame();
3907eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker
3917eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            final Matrix4f ortho = new Matrix4f();
3927eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
3937eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker
3947eafcae5ffb7f6f12ee573dea685dc6989a0ee91Jeff Tinker            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
3959a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
3969a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int texture = loadTexture(mBackground);
3979a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int program = buildProgram(sSimpleVS, sSimpleFS);
3989a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
3999a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int attribPosition = glGetAttribLocation(program, "position");
4009a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int attribTexCoords = glGetAttribLocation(program, "texCoords");
4019a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int uniformTexture = glGetUniformLocation(program, "texture");
4029a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            final int uniformProjection = glGetUniformLocation(program, "projection");
4039a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
4049a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            checkGlError();
4059a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
4069a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glViewport(0, 0, frame.width(), frame.height());
4079a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glBindTexture(GL_TEXTURE_2D, texture);
4089a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
4099a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glUseProgram(program);
4109a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glEnableVertexAttribArray(attribPosition);
4119a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glEnableVertexAttribArray(attribTexCoords);
4129a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glUniform1i(uniformTexture, 0);
4139a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
4149a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
4159a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker            checkGlError();
4169a498ef11566a5ae82c24b0651c58280309dfd04Jeff Tinker
41756c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker            if (w < 0 || h < 0) {
41856c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
41956c78c47d8c22621a8a23375d8a6c63d99a9335dJeff Tinker                glClear(GL_COLOR_BUFFER_BIT);
420            }
421
422            // drawQuad
423            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
424            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
425                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
426
427            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
428            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
429                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
430
431            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
432
433            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
434                throw new RuntimeException("Cannot swap buffers");
435            }
436            checkEglError();
437
438            finishGL();
439
440            return true;
441        }
442
443        private FloatBuffer createMesh(int left, int top, float right, float bottom) {
444            final float[] verticesData = {
445                    // X, Y, Z, U, V
446                     left,  bottom, 0.0f, 0.0f, 1.0f,
447                     right, bottom, 0.0f, 1.0f, 1.0f,
448                     left,  top,    0.0f, 0.0f, 0.0f,
449                     right, top,    0.0f, 1.0f, 0.0f,
450            };
451
452            final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
453            final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
454                    ByteOrder.nativeOrder()).asFloatBuffer();
455            triangleVertices.put(verticesData).position(0);
456            return triangleVertices;
457        }
458
459        private int loadTexture(Bitmap bitmap) {
460            int[] textures = new int[1];
461
462            glActiveTexture(GL_TEXTURE0);
463            glGenTextures(1, textures, 0);
464            checkGlError();
465
466            int texture = textures[0];
467            glBindTexture(GL_TEXTURE_2D, texture);
468            checkGlError();
469
470            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
471            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
472
473            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
474            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
475
476            GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
477            checkGlError();
478
479            bitmap.recycle();
480
481            return texture;
482        }
483
484        private int buildProgram(String vertex, String fragment) {
485            int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
486            if (vertexShader == 0) return 0;
487
488            int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
489            if (fragmentShader == 0) return 0;
490
491            int program = glCreateProgram();
492            glAttachShader(program, vertexShader);
493            checkGlError();
494
495            glAttachShader(program, fragmentShader);
496            checkGlError();
497
498            glLinkProgram(program);
499            checkGlError();
500
501            int[] status = new int[1];
502            glGetProgramiv(program, GL_LINK_STATUS, status, 0);
503            if (status[0] != GL_TRUE) {
504                String error = glGetProgramInfoLog(program);
505                Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
506                glDeleteShader(vertexShader);
507                glDeleteShader(fragmentShader);
508                glDeleteProgram(program);
509                return 0;
510            }
511
512            return program;
513        }
514
515        private int buildShader(String source, int type) {
516            int shader = glCreateShader(type);
517
518            glShaderSource(shader, source);
519            checkGlError();
520
521            glCompileShader(shader);
522            checkGlError();
523
524            int[] status = new int[1];
525            glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
526            if (status[0] != GL_TRUE) {
527                String error = glGetShaderInfoLog(shader);
528                Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
529                glDeleteShader(shader);
530                return 0;
531            }
532
533            return shader;
534        }
535
536        private void checkEglError() {
537            int error = mEgl.eglGetError();
538            if (error != EGL_SUCCESS) {
539                Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
540            }
541        }
542
543        private void checkGlError() {
544            int error = glGetError();
545            if (error != GL_NO_ERROR) {
546                Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
547            }
548        }
549
550        private void finishGL() {
551            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
552            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
553            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
554        }
555
556        private boolean initGL(SurfaceHolder surfaceHolder) {
557            mEgl = (EGL10) EGLContext.getEGL();
558
559            mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
560            if (mEglDisplay == EGL_NO_DISPLAY) {
561                throw new RuntimeException("eglGetDisplay failed " +
562                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
563            }
564
565            int[] version = new int[2];
566            if (!mEgl.eglInitialize(mEglDisplay, version)) {
567                throw new RuntimeException("eglInitialize failed " +
568                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
569            }
570
571            mEglConfig = chooseEglConfig();
572            if (mEglConfig == null) {
573                throw new RuntimeException("eglConfig not initialized");
574            }
575
576            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
577
578            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
579
580            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
581                int error = mEgl.eglGetError();
582                if (error == EGL_BAD_NATIVE_WINDOW) {
583                    Log.e(GL_LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
584                    return false;
585                }
586                throw new RuntimeException("createWindowSurface failed " +
587                        GLUtils.getEGLErrorString(error));
588            }
589
590            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
591                throw new RuntimeException("eglMakeCurrent failed " +
592                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
593            }
594
595            mGL = mEglContext.getGL();
596
597            return true;
598        }
599
600
601        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
602            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
603            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
604        }
605
606        private EGLConfig chooseEglConfig() {
607            int[] configsCount = new int[1];
608            EGLConfig[] configs = new EGLConfig[1];
609            int[] configSpec = getConfig();
610            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
611                throw new IllegalArgumentException("eglChooseConfig failed " +
612                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
613            } else if (configsCount[0] > 0) {
614                return configs[0];
615            }
616            return null;
617        }
618
619        private int[] getConfig() {
620            return new int[] {
621                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
622                    EGL_RED_SIZE, 8,
623                    EGL_GREEN_SIZE, 8,
624                    EGL_BLUE_SIZE, 8,
625                    EGL_ALPHA_SIZE, 0,
626                    EGL_DEPTH_SIZE, 0,
627                    EGL_STENCIL_SIZE, 0,
628                    EGL_NONE
629            };
630        }
631    }
632}
633