/* * Copyright (C) Winson Chiu * * 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 cw.kop.autobackground; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.media.effect.Effect; import android.media.effect.EffectContext; import android.media.effect.EffectFactory; import android.opengl.GLES20; import android.opengl.GLUtils; import android.util.Log; import android.view.animation.OvershootInterpolator; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Random; import cw.kop.autobackground.settings.AppSettings; /** * Created by TheKeeperOfPie on 11/15/2014. */ public class RenderImage { public static final String TAG = RenderImage.class.getCanonicalName(); public static int[] maxTextureSize = new int[1]; private static int mPositionHandle; private static int mTexCoordLoc; private static int mtrxhandle; private static int mAlphaHandle; private static int program; private float vertices[]; private short indices[]; private float uvs[]; private FloatBuffer vertexBuffer; private ShortBuffer drawListBuffer; private FloatBuffer uvBuffer; private float renderScreenWidth; private float renderScreenHeight; private float[] matrixProjection = new float[16]; private float[] matrixView = new float[16]; private float[] matrixProjectionAndView = new float[16]; private float[] transMatrix = new float[16]; private Bitmap imageBitmap; private boolean textureLoaded = false; private boolean animated = false; private int[] textureNames; private float scaleFactor = 1.0f; private float saveScaleFactor = 0f; private float offsetX = 0f; private float offsetY = 0f; private float minRatioX = 0f; private float minRatioY = 1f; private float maxRatioX = 0f; private float maxRatioY = 1f; private float rawOffsetX = -1f; private float saveOffsetX = 0f; private float saveOffsetY = 0f; private float bitmapWidth = 1f; private float bitmapHeight = 1f; private float animationModifierX = 0f; private float animationModifierY = 0f; private float angle = 0f; private float alpha = 0f; private volatile boolean inStartTransition = true; private volatile boolean inEndTransition = false; private long transitionEndtime = 0; private EffectFactory effectFactory; private EventListener eventListener; public RenderImage(Bitmap bitmap, int textureName, EventListener eventListener, float minRatioX, float maxRatioX, float minRatioY, float maxRatioY) { this.minRatioX = minRatioX; this.maxRatioX = maxRatioX; this.minRatioY = minRatioY; this.maxRatioY = maxRatioY; this.eventListener = eventListener; setBitmap(bitmap); textureNames = new int[] {textureName, 0}; uvs = new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f }; ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4); bb.order(ByteOrder.nativeOrder()); uvBuffer = bb.asFloatBuffer(); uvBuffer.put(uvs); uvBuffer.position(0); indices = new short[] {0, 1, 2, 0, 2, 3}; // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2); dlb.order(ByteOrder.nativeOrder()); drawListBuffer = dlb.asShortBuffer(); drawListBuffer.put(indices); drawListBuffer.position(0); } public static void setupRenderValues() { int vertexShader = GLShaders.loadShader(GLES20.GL_VERTEX_SHADER, GLShaders.VERTEX_SHADER); int fragmentShader = GLShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, GLShaders.FRAGMENT_SHADER); program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); mPositionHandle = GLES20.glGetAttribLocation(program, "vPosition"); mTexCoordLoc = GLES20.glGetAttribLocation(program, "aTexCoords"); mtrxhandle = GLES20.glGetUniformLocation(program, "uMVPMatrix"); mAlphaHandle = GLES20.glGetUniformLocation(program, "opacity"); GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0); } public void setDimensions(float width, float height) { renderScreenWidth = width; renderScreenHeight = height; applyScaleFactor(0.0f); } public void resetMatrices() { for (int i = 0; i < 16; i++) { matrixProjection[i] = 0.0f; matrixView[i] = 0.0f; matrixProjectionAndView[i] = 0.0f; } } public void setBitmap(Bitmap bitmap) { imageBitmap = bitmap; bitmapWidth = imageBitmap.getWidth(); bitmapHeight = imageBitmap.getHeight(); textureLoaded = false; } public void setAnimated(boolean animated) { this.animated = animated; } public void setAnimationModifierX(float animationModifierX) { this.animationModifierX = animationModifierX; } public void setAnimationModifierY(float animationModifierY) { this.animationModifierY = animationModifierY; } public void setAlpha(float alpha) { this.alpha = alpha; } public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) { if (AppSettings.forceParallax()) { if (AppSettings.useDrag() || animated) { offsetX += (renderScreenWidth - bitmapWidth) * scaleFactor * (1.0f - xOffset - rawOffsetX); } else { offsetX = (renderScreenWidth - bitmapWidth) * scaleFactor * (1.0f - xOffset); } rawOffsetX = 1.0f - xOffset; } else { if (AppSettings.useDrag() || animated) { offsetX += (renderScreenWidth - bitmapWidth) * scaleFactor * (xOffset - rawOffsetX); } else { offsetX = (renderScreenWidth - bitmapWidth) * scaleFactor * (xOffset); } rawOffsetX = xOffset; } } public void onSwipe(float xMovement, float yMovement) { if (AppSettings.reverseDrag()) { offsetX += xMovement; offsetY -= yMovement; } else { offsetX -= xMovement; offsetY += yMovement; } } public void loadTexture() { if (imageBitmap == null || textureLoaded) { return; } try { Log.i(TAG, "imageBitmap width: " + imageBitmap.getWidth() + " imageBitmap height: " + imageBitmap.getHeight()); GLES20.glGenTextures(2, textureNames, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureNames[0]); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, imageBitmap, 0); if (AppSettings.useEffects()) { effectFactory = EffectContext.createWithCurrentGlContext().getFactory(); initEffects(); } if (rawOffsetX > -1) { offsetX = rawOffsetX * (renderScreenWidth - bitmapWidth); Log.i(TAG, "rawOffsetX: " + rawOffsetX); Log.i(TAG, "value: " + -(bitmapWidth - renderScreenWidth)); } offsetY = -(bitmapHeight / renderScreenHeight) / 2; vertices = new float[] { 0.0f, bitmapHeight, 0.0f, 0.0f, 0.0f, 0.0f, bitmapWidth, 0.0f, 0.0f, bitmapWidth, bitmapHeight, 0.0f }; // The vertex buffer. ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); checkGLError("texImage2D: "); eventListener.doneLoading(); textureLoaded = true; imageBitmap.recycle(); imageBitmap = null; System.gc(); } catch (IllegalArgumentException e) { e.printStackTrace(); Log.i(TAG, "Error loading next image"); } catch (NullPointerException e) { e.printStackTrace(); Log.w(TAG, "Null on loading image error"); } } private void initEffects() { Random random = new Random(); boolean randomApplied = false; if (random.nextDouble() <= AppSettings.getRandomEffectsFrequency()) { if (AppSettings.useRandomEffects()) { applyRandomEffects(AppSettings.getRandomEffect()); randomApplied = true; if (AppSettings.useEffectsOverride()) { applyManualEffects(); } } } if (random.nextDouble() > AppSettings.getEffectsFrequency()) { eventListener.toastEffect("Not applied", ""); } else if (!randomApplied) { applyManualEffects(); } } private void applyEffect(Effect setEffect, String name, String description) { GLES20.glGenTextures(1, textureNames, 1); setEffect.apply(textureNames[0], Math.round(renderScreenWidth), Math.round(renderScreenHeight), textureNames[1]); setEffect.release(); GLES20.glDeleteTextures(1, textureNames, 0); textureNames[0] = textureNames[1]; if (AppSettings.useToast() && AppSettings.useToastEffects()) { eventListener.toastEffect(name, description); } Log.i(TAG, "Effect applied: " + name + "\n" + description); } private void applyManualEffects() { Effect effect; if (AppSettings.getAutoFixEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_AUTOFIX)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX); effect.setParameter("scale", AppSettings.getAutoFixEffect()); applyEffect(effect, "Auto Fix", "Value:" + AppSettings.getAutoFixEffect()); } if (AppSettings.getBrightnessEffect() != 1.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_BRIGHTNESS)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_BRIGHTNESS); effect.setParameter("brightness", AppSettings.getBrightnessEffect()); applyEffect(effect, "Brightness", "Value:" + AppSettings.getBrightnessEffect()); } if (AppSettings.getContrastEffect() != 1.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_CONTRAST)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_CONTRAST); effect.setParameter("contrast", AppSettings.getContrastEffect()); applyEffect(effect, "Contrast", "Value:" + AppSettings.getContrastEffect()); } if (AppSettings.getCrossProcessEffect() && EffectFactory.isEffectSupported( EffectFactory.EFFECT_CROSSPROCESS)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS); applyEffect(effect, "Cross Process", ""); } if (AppSettings.getDocumentaryEffect() && EffectFactory.isEffectSupported( EffectFactory.EFFECT_DOCUMENTARY)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY); applyEffect(effect, "Documentary", ""); } if (AppSettings.getDuotoneEffect() && EffectFactory.isEffectSupported(EffectFactory.EFFECT_DUOTONE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); effect.setParameter("first_color", AppSettings.getDuotoneColor(1)); effect.setParameter("second_color", AppSettings.getDuotoneColor(2)); applyEffect(effect, "Dual Tone", "\nColor 1: " + AppSettings.getDuotoneColor(1) + "\nColor 2: " + AppSettings.getDuotoneColor( 2)); } if (AppSettings.getFillLightEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_FILLLIGHT)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_FILLLIGHT); effect.setParameter("strength", AppSettings.getFillLightEffect()); applyEffect(effect, "Fill Light", "Value:" + AppSettings.getFillLightEffect()); } if (AppSettings.getFisheyeEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_FISHEYE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE); effect.setParameter("scale", AppSettings.getFisheyeEffect()); applyEffect(effect, "Fisheye", "Value:" + AppSettings.getFisheyeEffect()); } if (AppSettings.getGrainEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_GRAIN)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN); effect.setParameter("strength", AppSettings.getGrainEffect()); applyEffect(effect, "Grain", "Value:" + AppSettings.getGrainEffect()); } if (AppSettings.getGrayscaleEffect() && EffectFactory.isEffectSupported( EffectFactory.EFFECT_GRAYSCALE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE); applyEffect(effect, "Grayscale", ""); } if (AppSettings.getLomoishEffect() && EffectFactory.isEffectSupported(EffectFactory.EFFECT_LOMOISH)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH); applyEffect(effect, "Lomoish", ""); } if (AppSettings.getNegativeEffect() && EffectFactory.isEffectSupported(EffectFactory.EFFECT_NEGATIVE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE); applyEffect(effect, "Negaative", ""); } if (AppSettings.getPosterizeEffect() && EffectFactory.isEffectSupported( EffectFactory.EFFECT_POSTERIZE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE); applyEffect(effect, "Posterize", ""); } if (AppSettings.getSaturateEffect() != 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_SATURATE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE); effect.setParameter("scale", AppSettings.getSaturateEffect()); applyEffect(effect, "Saturate", "Value:" + AppSettings.getSaturateEffect()); } if (AppSettings.getSepiaEffect() && EffectFactory.isEffectSupported(EffectFactory.EFFECT_SEPIA)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA); applyEffect(effect, "Sepia", "Value:"); } if (AppSettings.getSharpenEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_SHARPEN)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_SHARPEN); effect.setParameter("scale", AppSettings.getSharpenEffect()); applyEffect(effect, "Sharpen", "Value:" + AppSettings.getSharpenEffect()); } if (AppSettings.getTemperatureEffect() != 0.5f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_TEMPERATURE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE); effect.setParameter("scale", AppSettings.getTemperatureEffect()); applyEffect(effect, "Temperature", "Value:" + AppSettings.getTemperatureEffect()); } if (AppSettings.getVignetteEffect() > 0.0f && EffectFactory.isEffectSupported( EffectFactory.EFFECT_VIGNETTE)) { effect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE); effect.setParameter("scale", AppSettings.getVignetteEffect()); applyEffect(effect, "Vignette", "Value:" + AppSettings.getVignetteEffect()); } } private void applyRandomEffects(String randomEffect) { Random random = new Random(); Effect effect; switch (randomEffect) { case "Completely Random": { String[] allEffectsList = eventListener.getContext().getResources().getStringArray( R.array.effects_list); String[] allEffectParameters = eventListener.getContext().getResources().getStringArray( R.array.effects_list_parameters); ArrayList<String> usableEffectsList = new ArrayList<>(); ArrayList<String> usableEffectsParameters = new ArrayList<>(); for (int i = 0; i < allEffectsList.length; i++) { if (EffectFactory.isEffectSupported(allEffectsList[i])) { usableEffectsList.add(allEffectsList[i]); usableEffectsParameters.add(allEffectParameters[i]); } } int index = random.nextInt(usableEffectsList.size()); String effectName = usableEffectsList.get(index); String parameter = usableEffectsParameters.get(index); float value = 0.0f; effect = effectFactory.createEffect(effectName); if (usableEffectsList.get(index).equals( "android.media.effect.effects.SaturateEffect")) { value = (random.nextFloat() * 0.6f) - 0.3f; } else if (usableEffectsList.get(index).equals( "android.media.effect.effects.ColorTemperatureEffect")) { value = random.nextFloat(); } else if (parameter.equals("brightness") || parameter.equals("contrast")) { value = (random.nextFloat() * 0.4f) + 0.8f; } else if (!usableEffectsParameters.get(index).equals("none")) { value = (random.nextFloat() * 0.3f) + 0.3f; } if (EffectFactory.isEffectSupported(effectName)) { if (value != 0.0f) { effect.setParameter(parameter, value); } applyEffect(effect, effectName.substring(effectName.indexOf("effects.") + 8), ((value != 0.0f) ? "Value:" + value : "")); } break; } case "Filter Effects": { String[] filtersList = eventListener.getContext().getResources().getStringArray( R.array.effects_filters_list); int index = random.nextInt(filtersList.length); effect = effectFactory.createEffect(filtersList[index]); applyEffect(effect, filtersList[index].substring(filtersList[index].indexOf( "effects.") + 8), ""); break; } case "Dual Tone Random": { int firstColor = Color.argb(255, random.nextInt(80), random.nextInt(80), random.nextInt(80)); int secondColor = Color.argb(255, random.nextInt(100) + 75, random.nextInt(100) + 75, random.nextInt(100) + 75); effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); effect.setParameter("first_color", firstColor); effect.setParameter("second_color", secondColor); applyEffect(effect, randomEffect, "\n" + firstColor + "\n" + secondColor); break; } case "Dual Tone Rainbow": { ArrayList<String> colorsList = (ArrayList<String>) Arrays.asList( eventListener.getContext().getResources().getStringArray(R.array.effects_color_list)); Collections.shuffle(colorsList); int firstColor = Color.parseColor(colorsList.get(0)); int secondColor = Color.parseColor(colorsList.get(1)); if (AppSettings.useDuotoneGray()) { firstColor = Color.parseColor("gray"); Log.i(TAG, "Duotone gray"); } effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); effect.setParameter("first_color", firstColor); effect.setParameter("second_color", secondColor); applyEffect(effect, randomEffect, "\n" + firstColor + "\n" + secondColor); break; } case "Dual Tone Warm": { int firstColor = Color.argb(255, random.nextInt(40) + 40, random.nextInt(40), random.nextInt(40)); int secondColor = Color.argb(255, random.nextInt(80) + 150, random.nextInt(80) + 125, random.nextInt(80) + 125); if (AppSettings.useDuotoneGray()) { int grayValue = random.nextInt(50); firstColor = Color.argb(255, grayValue, grayValue, grayValue); Log.i(TAG, "Duotone gray"); } effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); effect.setParameter("first_color", firstColor); effect.setParameter("second_color", secondColor); applyEffect(effect, randomEffect, "\n" + firstColor + "\n" + secondColor); break; } case "Dual Tone Cool": { int firstColor = Color.argb(255, random.nextInt(40), random.nextInt(40) + 40, random.nextInt(40) + 40); int secondColor = Color.argb(255, random.nextInt(80) + 125, random.nextInt(80) + 150, random.nextInt(80) + 150); if (AppSettings.useDuotoneGray()) { int grayValue = random.nextInt(50); firstColor = Color.argb(255, grayValue, grayValue, grayValue); Log.i(TAG, "Duotone gray"); } effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); effect.setParameter("first_color", firstColor); effect.setParameter("second_color", secondColor); applyEffect(effect, randomEffect, "\n" + firstColor + "\n" + secondColor); break; } } } // private void toastEffect(final String effectName, final String effectValue) { // if (AppSettings.useToast() && AppSettings.useToastEffects()) { // Toast.makeText(eventListener.getContext(), // "Effect applied: " + effectName + " " + effectValue, // Toast.LENGTH_SHORT).show(); // } // } public void applyScaleFactor(float factor) { scaleFactor *= factor; float minScaleFactor; float scaledRenderHeight = renderScreenHeight * (maxRatioY - minRatioY); if (AppSettings.extendScale()) { if (renderScreenWidth / bitmapWidth > scaledRenderHeight / bitmapHeight) { minScaleFactor = scaledRenderHeight / bitmapHeight; } else { minScaleFactor = renderScreenWidth / bitmapWidth; } } else { if (renderScreenWidth / bitmapWidth > scaledRenderHeight / bitmapHeight) { minScaleFactor = renderScreenWidth / bitmapWidth; } else { minScaleFactor = scaledRenderHeight / bitmapHeight; } } scaleFactor = Math.max( minScaleFactor, Math.min(scaleFactor, 5.0f)); } private void checkGLError(String op) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, op + ": glError " + error); } } private void calculateBounds() { if (offsetX < (-bitmapWidth + renderScreenWidth / scaleFactor)) { animationModifierX = Math.abs(animationModifierX); offsetX = -bitmapWidth + renderScreenWidth / scaleFactor; } else if (offsetX > 0f) { animationModifierX = -Math.abs(animationModifierX); offsetX = 0f; } if (offsetY < (maxRatioY * renderScreenHeight / scaleFactor - bitmapHeight)) { animationModifierY = Math.abs(animationModifierY); offsetY = maxRatioY * renderScreenHeight / scaleFactor - bitmapHeight; } else if (offsetY > minRatioY * renderScreenHeight / scaleFactor) { animationModifierY = -Math.abs(animationModifierY); offsetY = minRatioY * renderScreenHeight / scaleFactor; } } public void renderImage() { if (!textureLoaded) { loadTexture(); } if (renderScreenWidth / scaleFactor == 0) { return; } GLES20.glUseProgram(program); if (animated) { float safety = AppSettings.getAnimationSafety(); if (AppSettings.useAnimation() && bitmapWidth - (renderScreenWidth / scaleFactor) > safety) { float animationFactor = 1; offsetX += animationFactor * ((AppSettings.scaleAnimationSpeed()) ? (animationModifierX / scaleFactor) : animationModifierX); } if (AppSettings.useVerticalAnimation() && bitmapHeight - (renderScreenHeight * (maxRatioY - minRatioY) / scaleFactor) > safety) { offsetY += ((AppSettings.scaleAnimationSpeed()) ? (animationModifierY / scaleFactor) : animationModifierY); } } calculateBounds(); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glEnable(GLES20.GL_SCISSOR_TEST); GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); GLES20.glScissor(0, (int) (minRatioY * renderScreenHeight), (int) renderScreenWidth, (int) ((maxRatioY - minRatioY) * renderScreenHeight)); GLES20.glUniform1f(mAlphaHandle, alpha); if (inStartTransition) { applyStartTransition(); } else if (inEndTransition) { applyEndTransition(); } android.opengl.Matrix.orthoM(matrixProjection, 0, 0, renderScreenWidth / scaleFactor, 0, renderScreenHeight / scaleFactor, 0, 1f); android.opengl.Matrix.setIdentityM(transMatrix, 0); android.opengl.Matrix.translateM(transMatrix, 0, renderScreenWidth / scaleFactor / 2, renderScreenHeight / scaleFactor / 2, 0); android.opengl.Matrix.rotateM(transMatrix, 0, angle, 0.0f, 0.0f, 1.0f); android.opengl.Matrix.translateM(transMatrix, 0, -renderScreenWidth / scaleFactor / 2, -renderScreenHeight / scaleFactor / 2, 0); android.opengl.Matrix.translateM(transMatrix, 0, offsetX, offsetY, 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureNames[0]); // Enable generic vertex attribute array GLES20.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); // Enable generic vertex attribute array GLES20.glEnableVertexAttribArray(mTexCoordLoc); // Prepare the texturecoordinates GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, uvBuffer); android.opengl.Matrix.multiplyMM(matrixProjectionAndView, 0, matrixView, 0, transMatrix, 0); android.opengl.Matrix.multiplyMM(matrixProjectionAndView, 0, matrixProjection, 0, transMatrix, 0); // Apply the projection and view transformation GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, matrixProjectionAndView, 0); // Draw the container GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer); // Disable vertex array GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.glDisableVertexAttribArray(mTexCoordLoc); GLES20.glDisable(GLES20.GL_BLEND); GLES20.glDisable(GLES20.GL_SCISSOR_TEST); } private void applyStartTransition() { long time = System.currentTimeMillis(); if (transitionEndtime == 0) { transitionEndtime = time + AppSettings.getTransitionSpeed() * 100; } else if (transitionEndtime < time) { transitionEndtime = 0; angle = 0.0f; alpha = 1.0f; applyScaleFactor(0.0f); eventListener.requestRender(); inStartTransition = false; return; } float timeRatio = (float) (transitionEndtime - time) / (AppSettings.getTransitionSpeed() * 100); float ratio = maxRatioY - minRatioY; float scaledRenderWidth = renderScreenWidth / scaleFactor; float scaledRenderHeight = renderScreenHeight / scaleFactor; if (AppSettings.useZoomIn()) { float minScaleFactor; float trueRenderHeight = renderScreenHeight * (maxRatioY - minRatioY); if (renderScreenWidth / bitmapWidth > trueRenderHeight / bitmapHeight) { minScaleFactor = renderScreenWidth / bitmapWidth; } else { minScaleFactor = trueRenderHeight / bitmapHeight; } scaleFactor = minScaleFactor * (1.0f - timeRatio); offsetX = rawOffsetX * (renderScreenWidth - bitmapWidth) + renderScreenWidth / 2 / scaleFactor * (timeRatio); offsetY = renderScreenHeight / 2 / scaleFactor * (timeRatio) + minRatioY * renderScreenHeight; } if (AppSettings.useOvershoot()) { OvershootInterpolator horizontalOvershootInterpolator = new OvershootInterpolator( AppSettings.getOvershootIntensity() / 10f); if (AppSettings.reverseOvershoot()) { offsetX = rawOffsetX * (scaledRenderWidth - bitmapWidth) + scaledRenderWidth - scaledRenderWidth * horizontalOvershootInterpolator.getInterpolation( 1.0f - timeRatio); animationModifierX = Math.abs(animationModifierX); } else { offsetX = rawOffsetX * (scaledRenderWidth - bitmapWidth) - scaledRenderWidth + scaledRenderWidth * horizontalOvershootInterpolator.getInterpolation( 1.0f - timeRatio); animationModifierX = -Math.abs(animationModifierX); } } if (AppSettings.useVerticalOvershoot()) { OvershootInterpolator verticalOvershootInterpolator = new OvershootInterpolator( AppSettings.getVerticalOvershootIntensity() / 10f); offsetY = (AppSettings.reverseVerticalOvershoot() ? scaledRenderHeight * minRatioY + scaledRenderHeight * ratio - (scaledRenderHeight * ratio * verticalOvershootInterpolator.getInterpolation( 1.0f - timeRatio)) : scaledRenderHeight * minRatioY - scaledRenderHeight * ratio + (scaledRenderHeight * ratio * verticalOvershootInterpolator.getInterpolation( 1.0f - timeRatio))); } if (AppSettings.useSpinIn()) { angle = AppSettings.reverseSpinIn() ? AppSettings.getSpinInAngle() / 10f * -timeRatio : AppSettings.getSpinInAngle() / 10f * timeRatio; GLES20.glScissor(0, 0, (int) renderScreenWidth, (int) renderScreenHeight); } if (AppSettings.useFade()) { alpha = 1.0f - timeRatio; } else { alpha = 1.0f; } } public void startFinish() { inEndTransition = true; Log.i(TAG, "inEndTransition: " + inEndTransition); } public void finishImmediately() { GLES20.glDeleteTextures(2, textureNames, 0); eventListener.removeSelf(this); } private void applyEndTransition() { long time = System.currentTimeMillis(); if (transitionEndtime == 0) { transitionEndtime = time + AppSettings.getTransitionSpeed() * 100; } else if (transitionEndtime < time) { GLES20.glDeleteTextures(2, textureNames, 0); eventListener.removeSelf(this); return; } if (saveOffsetX == 0) { saveOffsetX = offsetX; saveOffsetY = offsetY; saveScaleFactor = scaleFactor; } float timeRatio = (float) (transitionEndtime - time) / (AppSettings.getTransitionSpeed() * 100); if (AppSettings.useZoomOut()) { scaleFactor = saveScaleFactor * timeRatio; offsetX = saveOffsetX + renderScreenWidth / 2 / scaleFactor * (1.0f - timeRatio); offsetY = saveOffsetY + renderScreenHeight / 2 / scaleFactor * (1.0f - timeRatio); } if (AppSettings.useSpinOut()) { angle = AppSettings.reverseSpinOut() ? AppSettings.getSpinOutAngle() / 10f * -(1.0f - timeRatio) : AppSettings.getSpinOutAngle() / 10f * (1.0f - timeRatio); } if (AppSettings.useFade()) { alpha = timeRatio; } else { alpha = 0.0f; } } public void setRawOffsetX(float rawOffsetX) { this.rawOffsetX = rawOffsetX; } public interface EventListener { public void removeSelf(RenderImage image); public void doneLoading(); public Context getContext(); public void toastEffect(final String effectName, final String effectValue); public void requestRender(); } }