Previously the call to glClear was executed when there was no available space (i.e. space which the Bitmap would *not* be drawn in on the background of the launcher), rather than when there was. Inverting the checks fixes this problem. Also, remove unnecessary Handler since the call to updateWallpapers is done via one-way binder interface and wrapped in a synchronize block anyways. Putting it on the Handler just put it on the main looper of the context that created WallpaperManager, which isn't necessary. Change-Id: Ic7a323303ec6e354d1ef245eec3434ff7128432d
714 lines
26 KiB
Java
714 lines
26 KiB
Java
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.systemui;
|
|
|
|
import static android.opengl.GLES20.*;
|
|
import static javax.microedition.khronos.egl.EGL10.*;
|
|
|
|
import android.app.ActivityManager;
|
|
import android.app.WallpaperManager;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.ComponentCallbacks2;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Region.Op;
|
|
import android.opengl.GLUtils;
|
|
import android.os.SystemProperties;
|
|
import android.renderscript.Matrix4f;
|
|
import android.service.wallpaper.WallpaperService;
|
|
import android.util.Log;
|
|
import android.view.MotionEvent;
|
|
import android.view.SurfaceHolder;
|
|
import android.view.WindowManager;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.FloatBuffer;
|
|
|
|
import javax.microedition.khronos.egl.EGL10;
|
|
import javax.microedition.khronos.egl.EGLConfig;
|
|
import javax.microedition.khronos.egl.EGLContext;
|
|
import javax.microedition.khronos.egl.EGLDisplay;
|
|
import javax.microedition.khronos.egl.EGLSurface;
|
|
|
|
/**
|
|
* Default built-in wallpaper that simply shows a static image.
|
|
*/
|
|
@SuppressWarnings({"UnusedDeclaration"})
|
|
public class ImageWallpaper extends WallpaperService {
|
|
private static final String TAG = "ImageWallpaper";
|
|
private static final String GL_LOG_TAG = "ImageWallpaperGL";
|
|
private static final boolean DEBUG = false;
|
|
private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
|
|
|
|
static final boolean FIXED_SIZED_SURFACE = true;
|
|
static final boolean USE_OPENGL = true;
|
|
|
|
WallpaperManager mWallpaperManager;
|
|
|
|
DrawableEngine mEngine;
|
|
|
|
boolean mIsHwAccelerated;
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
|
|
|
|
//noinspection PointlessBooleanExpression,ConstantConditions
|
|
if (FIXED_SIZED_SURFACE && USE_OPENGL) {
|
|
if (!isEmulator()) {
|
|
mIsHwAccelerated = ActivityManager.isHighEndGfx();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTrimMemory(int level) {
|
|
if (mEngine != null) {
|
|
mEngine.trimMemory(level);
|
|
}
|
|
}
|
|
|
|
private static boolean isEmulator() {
|
|
return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
|
|
}
|
|
|
|
@Override
|
|
public Engine onCreateEngine() {
|
|
mEngine = new DrawableEngine();
|
|
return mEngine;
|
|
}
|
|
|
|
class DrawableEngine extends Engine {
|
|
static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
|
|
static final int EGL_OPENGL_ES2_BIT = 4;
|
|
|
|
// TODO: Not currently used, keeping around until we know we don't need it
|
|
@SuppressWarnings({"UnusedDeclaration"})
|
|
private WallpaperObserver mReceiver;
|
|
|
|
Bitmap mBackground;
|
|
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
|
|
int mLastRotation = -1;
|
|
float mXOffset;
|
|
float mYOffset;
|
|
|
|
boolean mVisible = true;
|
|
boolean mRedrawNeeded;
|
|
boolean mOffsetsChanged;
|
|
int mLastXTranslation;
|
|
int mLastYTranslation;
|
|
|
|
private EGL10 mEgl;
|
|
private EGLDisplay mEglDisplay;
|
|
private EGLConfig mEglConfig;
|
|
private EGLContext mEglContext;
|
|
private EGLSurface mEglSurface;
|
|
|
|
private static final String sSimpleVS =
|
|
"attribute vec4 position;\n" +
|
|
"attribute vec2 texCoords;\n" +
|
|
"varying vec2 outTexCoords;\n" +
|
|
"uniform mat4 projection;\n" +
|
|
"\nvoid main(void) {\n" +
|
|
" outTexCoords = texCoords;\n" +
|
|
" gl_Position = projection * position;\n" +
|
|
"}\n\n";
|
|
private static final String sSimpleFS =
|
|
"precision mediump float;\n\n" +
|
|
"varying vec2 outTexCoords;\n" +
|
|
"uniform sampler2D texture;\n" +
|
|
"\nvoid main(void) {\n" +
|
|
" gl_FragColor = texture2D(texture, outTexCoords);\n" +
|
|
"}\n\n";
|
|
|
|
private static final int FLOAT_SIZE_BYTES = 4;
|
|
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
|
|
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
|
|
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
|
|
|
|
class WallpaperObserver extends BroadcastReceiver {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onReceive");
|
|
}
|
|
|
|
mLastSurfaceWidth = mLastSurfaceHeight = -1;
|
|
mBackground = null;
|
|
mRedrawNeeded = true;
|
|
drawFrame();
|
|
}
|
|
}
|
|
|
|
public DrawableEngine() {
|
|
super();
|
|
setFixedSizeAllowed(true);
|
|
}
|
|
|
|
public void trimMemory(int level) {
|
|
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
|
|
mBackground != null && mIsHwAccelerated) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "trimMemory");
|
|
}
|
|
mBackground.recycle();
|
|
mBackground = null;
|
|
mWallpaperManager.forgetLoadedWallpaper();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(SurfaceHolder surfaceHolder) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onCreate");
|
|
}
|
|
|
|
super.onCreate(surfaceHolder);
|
|
|
|
// TODO: Don't need this currently because the wallpaper service
|
|
// will restart the image wallpaper whenever the image changes.
|
|
//IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
|
|
//mReceiver = new WallpaperObserver();
|
|
//registerReceiver(mReceiver, filter, null, mHandler);
|
|
|
|
updateSurfaceSize(surfaceHolder);
|
|
|
|
setOffsetNotificationsEnabled(false);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
if (mReceiver != null) {
|
|
unregisterReceiver(mReceiver);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
|
|
super.onDesiredSizeChanged(desiredWidth, desiredHeight);
|
|
SurfaceHolder surfaceHolder = getSurfaceHolder();
|
|
if (surfaceHolder != null) {
|
|
updateSurfaceSize(surfaceHolder);
|
|
}
|
|
}
|
|
|
|
void updateSurfaceSize(SurfaceHolder surfaceHolder) {
|
|
if (FIXED_SIZED_SURFACE) {
|
|
// Used a fixed size surface, because we are special. We can do
|
|
// this because we know the current design of window animations doesn't
|
|
// cause this to break.
|
|
surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
|
|
} else {
|
|
surfaceHolder.setSizeFromLayout();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onVisibilityChanged(boolean visible) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
|
|
}
|
|
|
|
if (mVisible != visible) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Visibility changed to visible=" + visible);
|
|
}
|
|
mVisible = visible;
|
|
drawFrame();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onTouchEvent(MotionEvent event) {
|
|
super.onTouchEvent(event);
|
|
}
|
|
|
|
@Override
|
|
public void onOffsetsChanged(float xOffset, float yOffset,
|
|
float xOffsetStep, float yOffsetStep,
|
|
int xPixels, int yPixels) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
|
|
+ ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
|
|
+ ", xPixels=" + xPixels + ", yPixels=" + yPixels);
|
|
}
|
|
|
|
if (mXOffset != xOffset || mYOffset != yOffset) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
|
|
}
|
|
mXOffset = xOffset;
|
|
mYOffset = yOffset;
|
|
mOffsetsChanged = true;
|
|
}
|
|
drawFrame();
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
|
|
}
|
|
|
|
super.onSurfaceChanged(holder, format, width, height);
|
|
|
|
drawFrame();
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceDestroyed(SurfaceHolder holder) {
|
|
super.onSurfaceDestroyed(holder);
|
|
mLastSurfaceWidth = mLastSurfaceHeight = -1;
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceCreated(SurfaceHolder holder) {
|
|
super.onSurfaceCreated(holder);
|
|
mLastSurfaceWidth = mLastSurfaceHeight = -1;
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "onSurfaceRedrawNeeded");
|
|
}
|
|
super.onSurfaceRedrawNeeded(holder);
|
|
|
|
drawFrame();
|
|
}
|
|
|
|
void drawFrame() {
|
|
SurfaceHolder sh = getSurfaceHolder();
|
|
final Rect frame = sh.getSurfaceFrame();
|
|
final int dw = frame.width();
|
|
final int dh = frame.height();
|
|
int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
|
|
getDefaultDisplay().getRotation();
|
|
boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
|
|
|
|
boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
|
|
if (!redrawNeeded && !mOffsetsChanged) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
|
|
+ "and offsets have not changed.");
|
|
}
|
|
return;
|
|
}
|
|
mLastRotation = newRotation;
|
|
|
|
// Load bitmap if it is not yet loaded or if it was loaded at a different size
|
|
if (mBackground == null || surfaceDimensionsChanged) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
|
|
mBackground + ", " +
|
|
((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
|
|
((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
|
|
dw + ", " + dh);
|
|
}
|
|
mWallpaperManager.forgetLoadedWallpaper();
|
|
updateWallpaperLocked();
|
|
if (mBackground == null) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Unable to load bitmap");
|
|
}
|
|
return;
|
|
}
|
|
if (DEBUG) {
|
|
if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
|
|
Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
|
|
dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
|
|
mBackground.getHeight());
|
|
}
|
|
}
|
|
}
|
|
|
|
final int availw = dw - mBackground.getWidth();
|
|
final int availh = dh - mBackground.getHeight();
|
|
int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
|
|
int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
|
|
|
|
mOffsetsChanged = false;
|
|
mRedrawNeeded = false;
|
|
if (surfaceDimensionsChanged) {
|
|
mLastSurfaceWidth = dw;
|
|
mLastSurfaceHeight = dh;
|
|
}
|
|
mLastXTranslation = xPixels;
|
|
mLastYTranslation = yPixels;
|
|
if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Suppressed drawFrame since the image has not "
|
|
+ "actually moved an integral number of pixels.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Redrawing wallpaper");
|
|
}
|
|
|
|
if (mIsHwAccelerated) {
|
|
if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
|
|
drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
|
|
}
|
|
} else {
|
|
drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
|
|
if (FIXED_SIZED_SURFACE) {
|
|
// If the surface is fixed-size, we should only need to
|
|
// draw it once and then we'll let the window manager
|
|
// position it appropriately. As such, we no longer needed
|
|
// the loaded bitmap. Yay!
|
|
// hw-accelerated path retains bitmap for faster rotation
|
|
mBackground = null;
|
|
mWallpaperManager.forgetLoadedWallpaper();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void updateWallpaperLocked() {
|
|
Throwable exception = null;
|
|
try {
|
|
mBackground = null;
|
|
mBackground = mWallpaperManager.getBitmap();
|
|
} catch (RuntimeException e) {
|
|
exception = e;
|
|
} catch (OutOfMemoryError e) {
|
|
exception = e;
|
|
}
|
|
|
|
if (exception != null) {
|
|
mBackground = null;
|
|
// Note that if we do fail at this, and the default wallpaper can't
|
|
// be loaded, we will go into a cycle. Don't do a build where the
|
|
// default wallpaper can't be loaded.
|
|
Log.w(TAG, "Unable to load wallpaper!", exception);
|
|
try {
|
|
mWallpaperManager.clear();
|
|
} catch (IOException ex) {
|
|
// now we're really screwed.
|
|
Log.w(TAG, "Unable reset to default wallpaper!", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
|
|
Canvas c = sh.lockCanvas();
|
|
if (c != null) {
|
|
try {
|
|
if (DEBUG) {
|
|
Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
|
|
}
|
|
|
|
c.translate(x, y);
|
|
if (w < 0 || h < 0) {
|
|
c.save(Canvas.CLIP_SAVE_FLAG);
|
|
c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
|
|
Op.DIFFERENCE);
|
|
c.drawColor(0xff000000);
|
|
c.restore();
|
|
}
|
|
if (mBackground != null) {
|
|
c.drawBitmap(mBackground, 0, 0, null);
|
|
}
|
|
} finally {
|
|
sh.unlockCanvasAndPost(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
|
|
if (!initGL(sh)) return false;
|
|
|
|
final float right = left + mBackground.getWidth();
|
|
final float bottom = top + mBackground.getHeight();
|
|
|
|
final Rect frame = sh.getSurfaceFrame();
|
|
final Matrix4f ortho = new Matrix4f();
|
|
ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
|
|
|
|
final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
|
|
|
|
final int texture = loadTexture(mBackground);
|
|
final int program = buildProgram(sSimpleVS, sSimpleFS);
|
|
|
|
final int attribPosition = glGetAttribLocation(program, "position");
|
|
final int attribTexCoords = glGetAttribLocation(program, "texCoords");
|
|
final int uniformTexture = glGetUniformLocation(program, "texture");
|
|
final int uniformProjection = glGetUniformLocation(program, "projection");
|
|
|
|
checkGlError();
|
|
|
|
glViewport(0, 0, frame.width(), frame.height());
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
|
|
glUseProgram(program);
|
|
glEnableVertexAttribArray(attribPosition);
|
|
glEnableVertexAttribArray(attribTexCoords);
|
|
glUniform1i(uniformTexture, 0);
|
|
glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
|
|
|
|
checkGlError();
|
|
|
|
if (w > 0 || h > 0) {
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
// drawQuad
|
|
triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
|
|
glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
|
|
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
|
|
|
|
triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
|
|
glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
|
|
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
|
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
|
|
checkEglError();
|
|
|
|
finishGL();
|
|
|
|
return status;
|
|
}
|
|
|
|
private FloatBuffer createMesh(int left, int top, float right, float bottom) {
|
|
final float[] verticesData = {
|
|
// X, Y, Z, U, V
|
|
left, bottom, 0.0f, 0.0f, 1.0f,
|
|
right, bottom, 0.0f, 1.0f, 1.0f,
|
|
left, top, 0.0f, 0.0f, 0.0f,
|
|
right, top, 0.0f, 1.0f, 0.0f,
|
|
};
|
|
|
|
final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
|
|
final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
|
|
ByteOrder.nativeOrder()).asFloatBuffer();
|
|
triangleVertices.put(verticesData).position(0);
|
|
return triangleVertices;
|
|
}
|
|
|
|
private int loadTexture(Bitmap bitmap) {
|
|
int[] textures = new int[1];
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glGenTextures(1, textures, 0);
|
|
checkGlError();
|
|
|
|
int texture = textures[0];
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
checkGlError();
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
|
|
checkGlError();
|
|
|
|
return texture;
|
|
}
|
|
|
|
private int buildProgram(String vertex, String fragment) {
|
|
int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
|
|
if (vertexShader == 0) return 0;
|
|
|
|
int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
|
|
if (fragmentShader == 0) return 0;
|
|
|
|
int program = glCreateProgram();
|
|
glAttachShader(program, vertexShader);
|
|
checkGlError();
|
|
|
|
glAttachShader(program, fragmentShader);
|
|
checkGlError();
|
|
|
|
glLinkProgram(program);
|
|
checkGlError();
|
|
|
|
int[] status = new int[1];
|
|
glGetProgramiv(program, GL_LINK_STATUS, status, 0);
|
|
if (status[0] != GL_TRUE) {
|
|
String error = glGetProgramInfoLog(program);
|
|
Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(fragmentShader);
|
|
glDeleteProgram(program);
|
|
return 0;
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
private int buildShader(String source, int type) {
|
|
int shader = glCreateShader(type);
|
|
|
|
glShaderSource(shader, source);
|
|
checkGlError();
|
|
|
|
glCompileShader(shader);
|
|
checkGlError();
|
|
|
|
int[] status = new int[1];
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
|
|
if (status[0] != GL_TRUE) {
|
|
String error = glGetShaderInfoLog(shader);
|
|
Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
|
|
glDeleteShader(shader);
|
|
return 0;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
|
|
private void checkEglError() {
|
|
int error = mEgl.eglGetError();
|
|
if (error != EGL_SUCCESS) {
|
|
Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
|
|
}
|
|
}
|
|
|
|
private void checkGlError() {
|
|
int error = glGetError();
|
|
if (error != GL_NO_ERROR) {
|
|
Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
|
|
}
|
|
}
|
|
|
|
private void finishGL() {
|
|
mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
|
|
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
|
|
mEgl.eglTerminate(mEglDisplay);
|
|
}
|
|
|
|
private boolean initGL(SurfaceHolder surfaceHolder) {
|
|
mEgl = (EGL10) EGLContext.getEGL();
|
|
|
|
mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
if (mEglDisplay == EGL_NO_DISPLAY) {
|
|
throw new RuntimeException("eglGetDisplay failed " +
|
|
GLUtils.getEGLErrorString(mEgl.eglGetError()));
|
|
}
|
|
|
|
int[] version = new int[2];
|
|
if (!mEgl.eglInitialize(mEglDisplay, version)) {
|
|
throw new RuntimeException("eglInitialize failed " +
|
|
GLUtils.getEGLErrorString(mEgl.eglGetError()));
|
|
}
|
|
|
|
mEglConfig = chooseEglConfig();
|
|
if (mEglConfig == null) {
|
|
throw new RuntimeException("eglConfig not initialized");
|
|
}
|
|
|
|
mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
|
|
if (mEglContext == EGL_NO_CONTEXT) {
|
|
throw new RuntimeException("createContext failed " +
|
|
GLUtils.getEGLErrorString(mEgl.eglGetError()));
|
|
}
|
|
|
|
int attribs[] = {
|
|
EGL_WIDTH, 1,
|
|
EGL_HEIGHT, 1,
|
|
EGL_NONE
|
|
};
|
|
EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
|
|
mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
|
|
|
|
int[] maxSize = new int[1];
|
|
Rect frame = surfaceHolder.getSurfaceFrame();
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
|
|
|
|
mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
|
|
|
|
if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
|
|
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
|
|
mEgl.eglTerminate(mEglDisplay);
|
|
Log.e(GL_LOG_TAG, "requested texture size " +
|
|
frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
|
|
maxSize[0] + "x" + maxSize[0]);
|
|
return false;
|
|
}
|
|
|
|
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
|
|
if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
|
|
int error = mEgl.eglGetError();
|
|
if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
|
|
Log.e(GL_LOG_TAG, "createWindowSurface returned " +
|
|
GLUtils.getEGLErrorString(error) + ".");
|
|
return false;
|
|
}
|
|
throw new RuntimeException("createWindowSurface failed " +
|
|
GLUtils.getEGLErrorString(error));
|
|
}
|
|
|
|
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
|
|
throw new RuntimeException("eglMakeCurrent failed " +
|
|
GLUtils.getEGLErrorString(mEgl.eglGetError()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
|
|
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
|
|
}
|
|
|
|
private EGLConfig chooseEglConfig() {
|
|
int[] configsCount = new int[1];
|
|
EGLConfig[] configs = new EGLConfig[1];
|
|
int[] configSpec = getConfig();
|
|
if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
|
|
throw new IllegalArgumentException("eglChooseConfig failed " +
|
|
GLUtils.getEGLErrorString(mEgl.eglGetError()));
|
|
} else if (configsCount[0] > 0) {
|
|
return configs[0];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private int[] getConfig() {
|
|
return new int[] {
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_ALPHA_SIZE, 0,
|
|
EGL_DEPTH_SIZE, 0,
|
|
EGL_STENCIL_SIZE, 0,
|
|
EGL_CONFIG_CAVEAT, EGL_NONE,
|
|
EGL_NONE
|
|
};
|
|
}
|
|
}
|
|
}
|