/* * Copyright (C) 2015 Jorge Ruesga * * 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.ruesga.android.wallpapers.photophase.transitions; import android.content.Context; import android.opengl.GLES20; import android.os.SystemClock; import android.util.Log; import android.view.animation.AccelerateInterpolator; import com.ruesga.android.wallpapers.photophase.PhotoFrame; import com.ruesga.android.wallpapers.photophase.textures.TextureManager; import com.ruesga.android.wallpapers.photophase.transitions.Transitions.TRANSITIONS; import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; /** * The base class of all transitions that can be applied to the {@link PhotoFrame} classes. */ public abstract class Transition { public static final long MAX_TRANSTION_TIME = 2500L; private final Context mContext; private final TextureManager mTextureManager; protected int[] mProgramHandlers; protected int[] mTextureHandlers; protected int[] mPositionHandlers; protected int[] mTextureCoordHandlers; protected int[] mMVPMatrixHandlers; protected PhotoFrame mTarget; protected PhotoFrame mTransitionTarget; private final int[] mVertexShader; private final int[] mFragmentShader; private long mTime; protected boolean mRunning; private AccelerateInterpolator mInterpolator; /** * Constructor of <code>Transition</code> * * @param ctx The current context * @param tm The current texture manager * @param vertexShader The vertex shaders of the programs * @param fragmentShader The fragment shaders of the programs */ public Transition(Context ctx, TextureManager tm, int[] vertexShader, int[] fragmentShader) { super(); mContext = ctx; mTextureManager = tm; mVertexShader = vertexShader; mFragmentShader = fragmentShader; // Compile every program if(mVertexShader.length != mFragmentShader.length) { throw new IllegalArgumentException("mVertexShader.length != mFragmentShader.length"); } int cc = mVertexShader.length; mProgramHandlers = new int[cc]; mTextureHandlers = new int[cc]; mPositionHandlers = new int[cc]; mTextureCoordHandlers = new int[cc]; mMVPMatrixHandlers = new int[cc]; for (int i = 0; i < cc; i++) { createProgram(i); } mInterpolator = new AccelerateInterpolator(); } /** * Method that requests to apply this transition. * * @param target The target photo frame */ public void select(PhotoFrame target) { mTarget = target; if (hasTransitionTarget()) { // Load the transition frame and request a picture for it mTransitionTarget = new PhotoFrame( target.getDisposition(), mTextureManager, mTarget.getFrameVertex(), mTarget.getPhotoVertex(), mTarget.getBackgroundColor()); } } /** * Method that returns the target of the transition. * * @return PhotoFrame The target of the transition */ public PhotoFrame getTarget() { return mTarget; } /** * Method that returns the transition target of the transition. * * @return PhotoFrame The transition target of the transition */ public PhotoFrame getTransitionTarget() { return mTransitionTarget; } /** * Method that returns if the transition is selectable for the passed frame. * * @param frame The frame which the transition should be applied to * @return boolean If the transition is selectable for the passed frame */ public boolean isSelectable(PhotoFrame frame) { return true; } /** * Method that resets the current status of the transition. */ public void reset() { mTime = -1; mRunning = true; } public void swapTargets() { PhotoFrame target = mTransitionTarget; mTransitionTarget = mTarget; mTarget = target; } /** * Method that request a new mode */ public void chooseMode() { } /** * Method that returns the type of transition. * * @return TRANSITIONS The type of transition */ public abstract TRANSITIONS getType(); /** * Returns the transition time */ public abstract float getTransitionTime(); /** * Method that requests to apply this transition. * * @param matrix The model-view-projection matrix * @param offset The x offset (only if Matrix.setIdentityM if applied to the matrix) */ public final void apply(float[] matrix, float offset) { // Check internal vars if (mTarget == null || mTarget.getPositionBuffer() == null || mTarget.getTextureBuffer() == null) { return; } if (hasTransitionTarget() && (mTransitionTarget == null || mTransitionTarget.getPositionBuffer() == null || mTransitionTarget.getTextureBuffer() == null)) { return; } // Set the time the first time if (mTime == -1) { mTime = SystemClock.uptimeMillis(); } float delta = getDelta(); applyTransition(delta, matrix, offset); mRunning = delta < 1; } protected abstract void applyTransition(float delta, float[] matrix, float offset); /** * Method that returns if the transition is being transition. * * @return boolean If the transition is being transition. */ public boolean isRunning() { return mRunning; } /** * Method that return if the transition has a secondary target * * @return boolean If the transition has a secondary target */ public boolean hasTransitionTarget() { return false; } /** * Method that creates the program */ private void createProgram(int index) { mProgramHandlers[index] = GLESUtil.createProgram( mContext.getResources(), mVertexShader[index], mFragmentShader[index]); mTextureHandlers[index] = GLES20.glGetUniformLocation(mProgramHandlers[index], "sTexture"); GLESUtil.glesCheckError("glGetUniformLocation"); mPositionHandlers[index] = GLES20.glGetAttribLocation(mProgramHandlers[index], "aPosition"); GLESUtil.glesCheckError("glGetAttribLocation"); mTextureCoordHandlers[index] = GLES20.glGetAttribLocation(mProgramHandlers[index], "aTextureCoord"); GLESUtil.glesCheckError("glGetAttribLocation"); mMVPMatrixHandlers[index] = GLES20.glGetUniformLocation(mProgramHandlers[index], "uMVPMatrix"); GLESUtil.glesCheckError("glGetUniformLocation"); } /** * Method that set the program to use * * @param index The index of the program to use */ protected void useProgram(int index) { if (!GLES20.glIsProgram(mProgramHandlers[index])) { createProgram(index); } GLES20.glUseProgram(mProgramHandlers[index]); GLESUtil.glesCheckError("glUseProgram()"); } /** * Method that requests to the transition to remove its internal references and resources. */ public void recycle() { int cc = mProgramHandlers.length; for (int i = 0; i < cc; i++) { if (GLES20.glIsProgram(mProgramHandlers[i])) { if (GLESUtil.DEBUG_GL_MEMOBJS) { Log.d(GLESUtil.DEBUG_GL_MEMOBJS_DEL_TAG, "glDeleteProgram: " + mProgramHandlers[i]); } GLES20.glDeleteProgram(mProgramHandlers[i]); GLESUtil.glesCheckError("glDeleteProgram"); } mProgramHandlers[i] = -1; mTextureHandlers[i] = -1; mPositionHandlers[i] = -1; mTextureCoordHandlers[i] = -1; mMVPMatrixHandlers[i] = -1; } mTransitionTarget = null; mTarget = null; } private float getDelta() { float delta = Math.min( SystemClock.uptimeMillis() - mTime, getTransitionTime()) / getTransitionTime(); return mInterpolator.getInterpolation(delta); } }