package com.ruesga.android.wallpapers.photophase.widgets;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.media.effect.Effect;
import android.media.effect.EffectContext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.AttributeSet;
import android.util.TypedValue;
import com.ruesga.android.wallpapers.photophase.PhotoFrame;
import com.ruesga.android.wallpapers.photophase.borders.Border;
import com.ruesga.android.wallpapers.photophase.borders.Borders;
import com.ruesga.android.wallpapers.photophase.effects.Effects;
import com.ruesga.android.wallpapers.photophase.model.Disposition;
import com.ruesga.android.wallpapers.photophase.textures.SimpleTextureManager;
import com.ruesga.android.wallpapers.photophase.transitions.Transition;
import com.ruesga.android.wallpapers.photophase.transitions.Transitions;
import com.ruesga.android.wallpapers.photophase.utils.GLESUtil;
import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class LivePreviewView extends GLSurfaceView {
private class Renderer implements GLSurfaceView.Renderer {
private static final long TRANSITION_TIMEOUT = 2500L;
private int mWidth = -1;
private int mHeight = -1;
private final float[] mMVPMatrix = new float[16];
private final float[] mProjMatrix = new float[16];
private final float[] mVMatrix = new float[16];
private GLColor mBackgroundColor;
private EffectContext mEffectContext;
private Effects mEffectsFactory;
private Borders mBordersFactory;
private SimpleTextureManager mTextureManager;
private Effect mEffect;
private Border mBorder;
private PhotoFrame mFrame;
private Transition mTransition;
private Transitions.TRANSITIONS mTransitionType = Transitions.TRANSITIONS.NO_TRANSITION;
private Effects.EFFECTS mEffectType = Effects.EFFECTS.NO_EFFECT;
private Borders.BORDERS mBorderType = Borders.BORDERS.NO_BORDER;
private boolean mRecycled;
private final Context mContext;
private final Object mLock = new Object();
public Renderer(Context context) {
mContext = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
TypedValue a = new TypedValue();
getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, a, true);
if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& a.type <= TypedValue.TYPE_LAST_COLOR_INT) {
mBackgroundColor = new GLColor(a.data);
} else {
mBackgroundColor = new GLColor(Color.WHITE);
}
// We have a 2d (fake) scenario, disable all unnecessary tests. Deep are
// necessary for some 3d effects
GLES20.glDisable(GL10.GL_DITHER);
GLESUtil.glesCheckError("glDisable");
GLES20.glDisable(GL10.GL_CULL_FACE);
GLESUtil.glesCheckError("glDisable");
GLES20.glEnable(GL10.GL_DEPTH_TEST);
GLESUtil.glesCheckError("glEnable");
GLES20.glDepthMask(false);
GLESUtil.glesCheckError("glDepthMask");
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLESUtil.glesCheckError("glDepthFunc");
// Recreate the effect contexts
recycle();
synchronized (mLock) {
mEffectContext = EffectContext.createWithCurrentGlContext();
mEffectsFactory = new Effects(mContext, mEffectContext);
mBordersFactory = new Borders(mContext, mEffectContext);
recreateContext();
boolean singleTexture = mTransitionType.equals(Transitions.TRANSITIONS.NO_TRANSITION);
mTextureManager = new SimpleTextureManager(mContext, mEffect, mBorder, singleTexture);
mTransition = Transitions.createTransition(mContext, mTextureManager, mTransitionType);
}
mRecycled = false;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mWidth = width;
mHeight = height;
mTextureManager.setTargetDimensions(new Rect(0, 0, mWidth, mHeight));
createNewFrame();
GLES20.glViewport(0, 0, mWidth, mHeight);
GLESUtil.glesCheckError("glViewport");
Matrix.frustumM(mProjMatrix, 0, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 2.0f);
}
private void recreateContext() {
mEffect = mEffectsFactory.getEffect(mEffectType);
mBorder = mBordersFactory.getBorder(mBorderType);
mBorder.mBgColor = mBackgroundColor;
}
private void createNewFrame() {
float[] frameVertices = new float[]{-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
synchronized (mLock) {
if (mFrame != null) {
mFrame.recycle();
}
mFrame = new PhotoFrame(new Disposition(), mTextureManager,
frameVertices, frameVertices, mBackgroundColor);
mTransition.select(mFrame);
}
}
@Override
public void onDrawFrame(GL10 gl) {
// Check whether we have a valid surface
if (!hasValidSurface()) {
return;
}
Matrix.setLookAtM(mVMatrix, 0, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Draw the background
if (!mRecycled) {
drawBackground();
// Draw the image
mTransition.apply(mMVPMatrix, 0);
if (!mTransitionType.equals(Transitions.TRANSITIONS.NO_TRANSITION)) {
if (!mTransition.isRunning()) {
if (getRenderMode() != RENDERMODE_WHEN_DIRTY) {
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
postDelayed(mTransitionRequestRenderer, TRANSITION_TIMEOUT);
} else {
if (getRenderMode() != RENDERMODE_CONTINUOUSLY) {
setRenderMode(RENDERMODE_CONTINUOUSLY);
}
}
}
}
}
private void drawBackground() {
GLES20.glClearColor(
mBackgroundColor.r,
mBackgroundColor.g,
mBackgroundColor.b,
mBackgroundColor.a);
GLESUtil.glesCheckError("glClearColor");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLESUtil.glesCheckError("glClear");
}
public void setEffect(Effects.EFFECTS effect) {
synchronized (mLock) {
mEffectType = effect;
}
}
public void setBorder(Borders.BORDERS border) {
synchronized (mLock) {
mBorderType = border;
}
}
public void setTransition(Transitions.TRANSITIONS transition) {
synchronized (mLock) {
mTransitionType = transition;
}
}
public void recreate() {
synchronized (mLock) {
recreateContext();
onSurfaceChanged(null, mWidth, mHeight);
}
}
public void requestTransition() {
synchronized (mLock) {
if (!mRecycled) {
if (mTransition.hasTransitionTarget()) {
mTransition.swapTargets();
}
mTransition.chooseMode();
mTransition.reset();
}
}
}
public void recycle() {
synchronized (mLock) {
mRecycled = true;
if (mEffectContext != null) {
mEffectContext.release();
mEffectContext = null;
}
if (mEffectsFactory != null) {
mEffectsFactory.release();
mEffectsFactory = null;
}
if (mBordersFactory != null) {
mBordersFactory.release();
mBordersFactory = null;
}
if (mTransition != null) {
mTransition.recycle();
mTransition = null;
}
if (mFrame != null) {
mFrame.recycle();
mFrame = null;
}
}
}
}
private final Runnable mTransitionRequestRenderer = new Runnable() {
@Override
public void run() {
mRenderer.requestTransition();
requestRender();
}
};
private boolean mRecycled;
private Renderer mRenderer;
public LivePreviewView(Context context) {
super(context);
init(context);
}
public LivePreviewView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mRecycled = false;
setEGLContextClientVersion(2);
setPreserveEGLContextOnPause(true);
setEGLConfigChooser(false);
getHolder().setFormat(PixelFormat.RGBA_8888);
mRenderer = new Renderer(context);
setRenderer(mRenderer);
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
public void setEffect(Effects.EFFECTS effect) {
mRenderer.setEffect(effect);
requestRender();
}
public void setBorder(Borders.BORDERS border) {
mRenderer.setBorder(border);
requestRender();
}
public void setTransition(Transitions.TRANSITIONS transition) {
mRenderer.setTransition(transition);
requestRender();
}
public void recreate() {
queueEvent(new Runnable() {
@Override
public void run() {
mRenderer.recreate();
requestRender();
}
});
}
@Override
public void onResume() {
super.onResume();
if (!mRecycled) {
requestRender();
}
}
public void recycle() {
mRecycled = true;
queueEvent(new Runnable() {
@Override
public void run() {
mRenderer.recycle();
}
});
}
private boolean hasValidSurface() {
try {
return getHolder().getSurface().isValid();
} catch (Exception ex) {
return false;
}
}
}