/*
* 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.shapes;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.opengl.GLES20;
import android.util.Log;
import com.ruesga.android.wallpapers.photophase.AndroidHelper;
import com.ruesga.android.wallpapers.photophase.R;
import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider;
import com.ruesga.android.wallpapers.photophase.utils.GLESUtil;
import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLColor;
import com.ruesga.android.wallpapers.photophase.utils.GLESUtil.GLESTextureInfo;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* A shape to draw an oops message
*/
public class OopsShape implements DrawableShape {
private static final int VERTEX_SHADER = R.raw.default_vertex_shader;
private static final int FRAGMENT_SHADER = R.raw.default_fragment_shader;
// The texture coordinates
private static final float[] TEXTURE_COORDS = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
// The vertex position coordinates
private static final float[] VERTEX_COORDS_PORTRAIT = {
-0.75f, -0.5f,
0.75f, -0.5f,
-0.75f, 0.5f,
0.75f, 0.5f
};
private static final float[] VERTEX_COORDS_LANDSCAPE = {
-0.5f, -0.75f,
0.5f, -0.75f,
-0.5f, 0.75f,
0.5f, 0.75f
};
private static Typeface sFont;
private final Context mContext;
private FloatBuffer mPositionBuffer;
private FloatBuffer mTextureBuffer;
private final int[] mProgramHandlers;
private final int[] mTextureHandlers;
private final int[] mPositionHandlers;
private final int[] mTextureCoordHandlers;
private final int[] mMVPMatrixHandlers;
private GLESTextureInfo mOopsImageTexture;
private GLESTextureInfo mOopsTextTexture;
private GLESTextureInfo mNoPermissionTextTexture;
/**
* Constructor of <code>OopsShape</code>
*
* @param ctx The current context
*/
public OopsShape(Context ctx) {
mContext = ctx;
if (sFont == null) {
sFont = Typeface.createFromAsset(ctx.getAssets(), "fonts/NotoSans-Bold.ttf");
}
int orientation = ctx.getResources().getConfiguration().orientation;
float[] vertex = VERTEX_COORDS_PORTRAIT;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
vertex = VERTEX_COORDS_LANDSCAPE;
}
// Load the buffers
ByteBuffer bb1 = ByteBuffer.allocateDirect(vertex.length * 4); // (# of coordinate values * 4 bytes per float)
bb1.order(ByteOrder.nativeOrder());
mPositionBuffer = bb1.asFloatBuffer();
mPositionBuffer.put(vertex);
mPositionBuffer.position(0);
// -
ByteBuffer bb2 = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4); // (# of coordinate values * 4 bytes per float)
bb2.order(ByteOrder.nativeOrder());
mTextureBuffer = bb2.asFloatBuffer();
mTextureBuffer.put(TEXTURE_COORDS);
mTextureBuffer.position(0);
// Initialize the structures
mProgramHandlers = new int[2];
mTextureHandlers = new int[2];
mPositionHandlers = new int[2];
mTextureCoordHandlers = new int[2];
mMVPMatrixHandlers = new int[2];
// Create all the params
for (int i = 0; i < 2; i++) {
mProgramHandlers[i] =
GLESUtil.createProgram(
ctx.getResources(), VERTEX_SHADER, FRAGMENT_SHADER);
mTextureHandlers[i] =
GLES20.glGetUniformLocation(mProgramHandlers[i], "sTexture");
mPositionHandlers[i] =
GLES20.glGetAttribLocation(mProgramHandlers[i], "aPosition");
GLESUtil.glesCheckError("glGetAttribLocation");
mTextureCoordHandlers[i] =
GLES20.glGetAttribLocation(mProgramHandlers[i], "aTextureCoord");
GLESUtil.glesCheckError("glGetAttribLocation");
mMVPMatrixHandlers[i] =
GLES20.glGetUniformLocation(mProgramHandlers[i], "uMVPMatrix");
GLESUtil.glesCheckError("glGetUniformLocation");
}
// Load the textures
mOopsImageTexture = GLESUtil.loadTexture(ctx, R.drawable.bg_oops, null, null, null, false);
Bitmap textBitmap = text2Bitmap(ctx.getString(R.string.no_pictures_oops_msg));
mOopsTextTexture = GLESUtil.loadTexture(mContext, textBitmap, null, null, null);
Bitmap noPermissionTextBitmap =
text2Bitmap(ctx.getString(R.string.no_pictures_permission_required_msg));
mNoPermissionTextTexture = GLESUtil.loadTexture(
mContext, noPermissionTextBitmap, null, null, null);
// Recycle
mOopsImageTexture.bitmap.recycle();
mOopsImageTexture.bitmap = null;
textBitmap.recycle();
noPermissionTextBitmap.recycle();
mOopsTextTexture.bitmap.recycle();
mOopsTextTexture.bitmap = null;
mNoPermissionTextTexture.bitmap.recycle();
mNoPermissionTextTexture.bitmap = null;
}
/**
* {@inheritDoc}
*/
@Override
public void draw(float[] matrix) {
// Bind default FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLESUtil.glesCheckError("glBindFramebuffer");
// Clear background
GLColor bg = PreferencesProvider.Preferences.General.DEFAULT_BACKGROUND_COLOR;
GLES20.glClearColor(bg.r, bg.g, bg.b, bg.a);
GLESUtil.glesCheckError("glClearColor");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLESUtil.glesCheckError("glClear");
// Enable blend
GLES20.glEnable(GLES20.GL_BLEND);
GLESUtil.glesCheckError("glEnable");
GLES20.glBlendFunc(GLES20.GL_SRC_COLOR, GLES20.GL_ONE_MINUS_SRC_COLOR);
GLESUtil.glesCheckError("glBlendFunc");
// Draw the textures
drawTexture(matrix, 0, mOopsImageTexture.handle);
if (AndroidHelper.hasReadExternalStoragePermissionGranted(mContext)) {
drawTexture(matrix, 1, mOopsTextTexture.handle);
} else {
drawTexture(matrix, 1, mNoPermissionTextTexture.handle);
}
// Disable blending
GLES20.glDisable(GLES20.GL_BLEND);
GLESUtil.glesCheckError("glDisable");
}
/**
* Method that draws a texture
*
* @param matrix The model-view-projection matrix
* @param index The index of the texture
* @param texture The texture handler
*/
private void drawTexture(float[] matrix, int index, int texture) {
// Use our shader program
GLES20.glUseProgram(mProgramHandlers[index]);
GLESUtil.glesCheckError("glUseProgram()");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandlers[index], 1, false, matrix, 0);
GLESUtil.glesCheckError("glUniformMatrix4fv");
// Texture
GLES20.glVertexAttribPointer(mTextureCoordHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);
GLESUtil.glesCheckError("glVertexAttribPointer");
GLES20.glEnableVertexAttribArray(mTextureCoordHandlers[index]);
GLESUtil.glesCheckError("glEnableVertexAttribArray");
// Position
GLES20.glVertexAttribPointer(mPositionHandlers[index], 2, GLES20.GL_FLOAT, false, 0, mPositionBuffer);
GLESUtil.glesCheckError("glVertexAttribPointer");
GLES20.glEnableVertexAttribArray(mPositionHandlers[index]);
GLESUtil.glesCheckError("glEnableVertexAttribArray");
// Set the input textures
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLESUtil.glesCheckError("glActiveTexture");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLESUtil.glesCheckError("glBindTexture");
GLES20.glUniform1i(mTextureHandlers[index], 0);
GLESUtil.glesCheckError("glUniform1i");
// Draw
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLESUtil.glesCheckError("glDrawElements");
// Disable attributes
GLES20.glDisableVertexAttribArray(mPositionHandlers[index]);
GLESUtil.glesCheckError("glDisableVertexAttribArray");
GLES20.glDisableVertexAttribArray(mTextureCoordHandlers[index]);
GLESUtil.glesCheckError("glDisableVertexAttribArray");
}
/**
* Method that requests to remove the internal references and resources.
*/
public void recycle() {
// Remove textures
if (mOopsImageTexture != null && mOopsImageTexture.handle != 0) {
if (GLESUtil.DEBUG_GL_MEMOBJS) {
Log.d(GLESUtil.DEBUG_GL_MEMOBJS_DEL_TAG, "glDeleteTextures: ["
+ mOopsImageTexture.handle + "]");
}
int[] textures = new int[]{mOopsImageTexture.handle};
GLES20.glDeleteTextures(1, textures, 0);
GLESUtil.glesCheckError("glDeleteTextures");
}
mOopsImageTexture = null;
if (mOopsTextTexture != null && mOopsTextTexture.handle != 0) {
int[] textures = new int[]{mOopsTextTexture.handle};
if (GLESUtil.DEBUG_GL_MEMOBJS) {
Log.d(GLESUtil.DEBUG_GL_MEMOBJS_DEL_TAG, "glDeleteTextures: ["
+ mOopsTextTexture.handle + "]");
}
GLES20.glDeleteTextures(1, textures, 0);
GLESUtil.glesCheckError("glDeleteTextures");
}
mOopsTextTexture = null;
if (mNoPermissionTextTexture != null && mNoPermissionTextTexture.handle != 0) {
int[] textures = new int[]{mNoPermissionTextTexture.handle};
if (GLESUtil.DEBUG_GL_MEMOBJS) {
Log.d(GLESUtil.DEBUG_GL_MEMOBJS_DEL_TAG, "glDeleteTextures: ["
+ mNoPermissionTextTexture.handle + "]");
}
GLES20.glDeleteTextures(1, textures, 0);
GLESUtil.glesCheckError("glDeleteTextures");
}
mNoPermissionTextTexture = null;
// Remove buffers
if (mPositionBuffer != null) {
mPositionBuffer.clear();
}
if (mTextureBuffer != null) {
mTextureBuffer.clear();
}
mPositionBuffer = null;
mTextureBuffer = null;
for (int i = 0; i < 2; 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(" + i + ")");
}
mProgramHandlers[i] = 0;
mTextureHandlers[i] = 0;
mPositionHandlers[i] = 0;
mTextureCoordHandlers[i] = 0;
mMVPMatrixHandlers[i] = 0;
}
}
/**
* Method that converts a text to a bitmap
*
* @param text The text to draw to the bitmap
* @return Bitmap The bitmap with the text
*/
private Bitmap text2Bitmap(String text) {
Paint paint = new Paint();
paint.setTypeface(sFont);
paint.setColor(Color.WHITE);
paint.setTextSize(mContext.getResources().getDimensionPixelSize(R.dimen.oops_shape_text_size));
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
Bitmap src = mOopsImageTexture.bitmap;
Bitmap image = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(image);
canvas.drawText(text, src.getWidth() / 2, src.getHeight() - (src.getHeight() * 0.33f), paint);
return image;
}
}