diff options
-rw-r--r-- | assets/fonts/Roboto-Bold.ttf | bin | 0 -> 163448 bytes | |||
-rw-r--r-- | res/drawable-nodpi/bg_cid_oops.png | bin | 0 -> 13522 bytes | |||
-rw-r--r-- | res/raw/mix_fragment_shader.glsl | 27 | ||||
-rw-r--r-- | res/raw/mix_vertex_shader.glsl | 27 | ||||
-rw-r--r-- | res/values/strings.xml | 3 | ||||
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/GLESUtil.java | 2 | ||||
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java | 2 | ||||
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java | 70 | ||||
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/TextureManager.java | 39 | ||||
-rw-r--r-- | src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java | 270 |
10 files changed, 409 insertions, 31 deletions
diff --git a/assets/fonts/Roboto-Bold.ttf b/assets/fonts/Roboto-Bold.ttf Binary files differnew file mode 100644 index 0000000..91ec212 --- /dev/null +++ b/assets/fonts/Roboto-Bold.ttf diff --git a/res/drawable-nodpi/bg_cid_oops.png b/res/drawable-nodpi/bg_cid_oops.png Binary files differnew file mode 100644 index 0000000..b5373da --- /dev/null +++ b/res/drawable-nodpi/bg_cid_oops.png diff --git a/res/raw/mix_fragment_shader.glsl b/res/raw/mix_fragment_shader.glsl new file mode 100644 index 0000000..df80f89 --- /dev/null +++ b/res/raw/mix_fragment_shader.glsl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ + +precision mediump float; + +varying vec2 vTextureCoord; +uniform sampler2D sTexture1; +uniform sampler2D sTexture2; + +void main() { + vec4 color1 = texture2D(sTexture1, vTextureCoord); + vec4 color2 = texture2D(sTexture1, vTextureCoord); + gl_FragColor = color2 * color1; +} diff --git a/res/raw/mix_vertex_shader.glsl b/res/raw/mix_vertex_shader.glsl new file mode 100644 index 0000000..dad4d5a --- /dev/null +++ b/res/raw/mix_vertex_shader.glsl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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. + */ + +uniform mat4 uMVPMatrix; + +attribute vec4 aPosition; +attribute vec2 aTextureCoord; + +varying vec2 vTextureCoord; + +void main() { + gl_Position = uMVPMatrix * aPosition; + vTextureCoord = aTextureCoord; +} diff --git a/res/values/strings.xml b/res/values/strings.xml index 7b7a071..e9708d3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -23,6 +23,9 @@ <!-- The app description --> <string name="app_description">PhotoPhase Live Wallpaper</string> + <!-- No images message --> + <string name="no_pictures_oops_msg">Oops, no pictures.</string> + <!-- Menus --> <string name="mnu_ok" translatable="false">@android:string/ok</string> <string name="mnu_restore">Restore</string> diff --git a/src/org/cyanogenmod/wallpapers/photophase/GLESUtil.java b/src/org/cyanogenmod/wallpapers/photophase/GLESUtil.java index 3183ff4..17b6602 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/GLESUtil.java +++ b/src/org/cyanogenmod/wallpapers/photophase/GLESUtil.java @@ -95,7 +95,7 @@ public final class GLESUtil { /** * Constructor of <code>GLColor</code> from ARGB. * - * @param argb An #AARRGGBB string + * @param argb An #AARRGGBB number */ public GLColor(int argb) { this.a = Color.alpha(argb) / MAX_COLOR; diff --git a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java index 11f9743..0c9ec0f 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java +++ b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java @@ -214,7 +214,7 @@ public class MediaPictureDiscoverer { } // Publish partial data - if (i % 5 == 0 && partial.size() > 0){ + if (i % 5 == 0 && partial.size() > 0) { publishProgress(partial.toArray(new File[partial.size()])); partial.clear(); } diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java index a4b1364..ffc4d22 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java @@ -41,6 +41,7 @@ import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider; import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; import org.cyanogenmod.wallpapers.photophase.preferences.TouchAction; import org.cyanogenmod.wallpapers.photophase.shapes.ColorShape; +import org.cyanogenmod.wallpapers.photophase.shapes.OopsShape; import org.cyanogenmod.wallpapers.photophase.transitions.Transition; import javax.microedition.khronos.egl.EGLConfig; @@ -66,6 +67,7 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { /*package*/ PhotoPhaseWallpaperWorld mWorld; /*package*/ ColorShape mOverlay; + /*package*/ OopsShape mOopsShape; /*package*/ long mLastRunningTransition; @@ -381,9 +383,11 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { if (mWorld != null) mWorld.recycle(); if (mTextureManager != null) mTextureManager.recycle(); if (mOverlay != null) mOverlay.recycle(); + if (mOopsShape != null) mOopsShape.recycle(); mWorld = null; mTextureManager = null; mOverlay = null; + mOopsShape = null; } } @@ -469,7 +473,7 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { } mWorld = new PhotoPhaseWallpaperWorld(mContext, mTextureManager); - // Create all the other shapes + // Create the overlay shape final float[] vertex = { -1.0f, -1.0f, 1.0f, -1.0f, @@ -478,7 +482,10 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { }; mOverlay = new ColorShape(mContext, vertex, Colors.getOverlay()); - // Set the viewport and the fustrum to use + // Create the Oops shape + mOopsShape = new OopsShape(mContext, R.string.no_pictures_oops_msg); + + // Set the viewport and the fustrum GLES20.glViewport(0, -statusBarHeight, width, height); GLESUtil.glesCheckError("glViewport"); Matrix.frustumM(mProjMatrix, 0, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 2.0f); @@ -497,32 +504,42 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { @Override public void onDrawFrame(GL10 glUnused) { synchronized (mDrawing) { - // Draw the background - drawBackground(); + // Set the projection, view and model + 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); + + if (mTextureManager != null) { + if (mTextureManager.getStatus() == 1 && mTextureManager.isEmpty()) { + // Advise the user and stop + drawOops(); + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - if (mWorld != null) { - // Set the projection, view and model - 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); + } else { + // Draw the background + drawBackground(); - // Now draw the world (all the photo frames with effects) - mWorld.draw(mMVPMatrix); + if (mWorld != null) { + // Now draw the world (all the photo frames with effects) + mWorld.draw(mMVPMatrix); - // Check if we have some pending transition or transition has exceed its timeout - if (!mWorld.hasRunningTransition() || isTransitionTimeout()) { - mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + // Check if we have some pending transition or transition has exceed its timeout + if (!mWorld.hasRunningTransition() || firedTransitionTimeout()) { + mDispatcher.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + + // Now start a delayed thread to generate the next effect + mHandler.removeCallbacks(mTransitionThread); + mWorld.deselectTransition(mMVPMatrix); + mLastRunningTransition = 0; + mHandler.postDelayed(mTransitionThread, + Preferences.General.Transitions.getTransitionInterval()); + } + } - // Now start a delayed thread to generate the next effect - mHandler.removeCallbacks(mTransitionThread); - mWorld.deselectTransition(mMVPMatrix); - mLastRunningTransition = 0; - mHandler.postDelayed(mTransitionThread, - Preferences.General.Transitions.getTransitionInterval()); + // Draw the overlay + drawOverlay(); } } - // Draw the overlay - drawOverlay(); } } @@ -531,7 +548,7 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { * * @return boolean if the transition has exceed the timeout */ - private boolean isTransitionTimeout() { + private boolean firedTransitionTimeout() { long now = System.currentTimeMillis(); long diff = now - mLastRunningTransition; return mLastRunningTransition != 0 && diff > Transition.MAX_TRANSTION_TIME; @@ -558,4 +575,13 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { } } + /** + * Method that draws the oops message + */ + private void drawOops() { + if (mOopsShape != null) { + mOopsShape.draw(mMVPMatrix); + } + } + } diff --git a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java index 6e5b577..a4ce157 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java +++ b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java @@ -57,6 +57,12 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { final GLESSurfaceDispatcher mDispatcher; + // The status of the texture manager: + // 0 - Loading + // 1 - Loaded + // 2 - Error + private byte mStatus; + /** * A private runnable that will run in the GLThread */ @@ -286,6 +292,7 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { @Override public void onStartMediaDiscovered(boolean userRequest) { // No images but thread should start here to received partial data + this.mStatus = 0; // Loading if (mBackgroundTask != null) { mBackgroundTask.setAvailableImages(new File[]{}); if (!mBackgroundTask.mRun) { @@ -318,13 +325,10 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { // load pictures in background if (mBackgroundTask != null) { mBackgroundTask.setAvailableImages(images); - if (!mBackgroundTask.mRun) { - mBackgroundTask.start(); - } else { - synchronized (mBackgroundTask.mLoadSync) { - mBackgroundTask.mLoadSync.notify(); - } + synchronized (mBackgroundTask.mLoadSync) { + mBackgroundTask.mLoadSync.notify(); } + this.mStatus = 1; // Loaded // Audit int found = images == null ? 0 : images.length; @@ -335,6 +339,8 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { R.plurals.msg_media_reload_complete, found).toString(), found); Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); } + } else { + this.mStatus = 2; // Error } } @@ -359,6 +365,25 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { mBackgroundTask = null; } + + /** + * Returns the status of the texture manager + * + * @return byte The status + */ + public byte getStatus() { + return mStatus; + } + + /** + * Returns if the texture manager is empty + * + * @return boolean If the texture manager is empty + */ + public boolean isEmpty() { + return mBackgroundTask != null && mBackgroundTask.mEmpty; + } + /** * An internal thread to load pictures in background */ @@ -368,7 +393,7 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { boolean mRun; boolean mTaskPaused; - private boolean mEmpty; + /*package*/ boolean mEmpty; private final List<File> mNewImages; private final List<File> mUsedImages; diff --git a/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java b/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java new file mode 100644 index 0000000..6d8ea1c --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/shapes/OopsShape.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase.shapes; + +import android.content.Context; +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 org.cyanogenmod.wallpapers.photophase.Colors; +import org.cyanogenmod.wallpapers.photophase.GLESUtil; +import org.cyanogenmod.wallpapers.photophase.GLESUtil.GLColor; +import org.cyanogenmod.wallpapers.photophase.GLESUtil.GLESTextureInfo; +import org.cyanogenmod.wallpapers.photophase.R; + +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 = { + -0.5f, -0.5f, + 0.5f, -0.5f, + -0.5f, 0.5f, + 0.5f, 0.5f + }; + + private FloatBuffer mPositionBuffer; + private FloatBuffer mTextureBuffer; + + private int[] mProgramHandlers; + private int[] mTextureHandlers; + private int[] mPositionHandlers; + private int[] mTextureCoordHandlers; + private int[] mMVPMatrixHandlers; + + private String mMessage; + + private GLESTextureInfo mOopsImageTexture; + private GLESTextureInfo mOopsTextTexture; + + /** + * Constructor of <code>OopsShape</code> + * + * @param ctx The current context + * @param resourceMessageId The resource identifier with the message + */ + public OopsShape(Context ctx, int resourceMessageId) { + super(); + + // Load the buffers + ByteBuffer bb1 = ByteBuffer.allocateDirect(VERTEX_COORDS.length * 4); // (# of coordinate values * 4 bytes per float) + bb1.order(ByteOrder.nativeOrder()); + mPositionBuffer = bb1.asFloatBuffer(); + mPositionBuffer.put(VERTEX_COORDS); + 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.glGetAttribLocation(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"); + } + + // Get the localized message + mMessage = ctx.getString(resourceMessageId); + + // Load the textures + mOopsImageTexture = GLESUtil.loadTexture(ctx, R.drawable.bg_cid_oops, null, null, false); + mOopsTextTexture = GLESUtil.loadTexture(text2Bitmap(ctx, mMessage), null, null); + } + + /** + * {@inheritDoc} + */ + @Override + public void draw(float[] matrix) { + // Bind default FBO + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Clear background + GLColor bg = Colors.getBackground(); + 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); + drawTexture(matrix, 1, mOopsTextTexture.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) { + 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}; + GLES20.glDeleteTextures(1, textures, 0); + GLESUtil.glesCheckError("glDeleteTextures"); + } + mOopsTextTexture = 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])) { + 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 ctx The current context + * @param text The text to draw to the bitmap + * @return Bitmap The bitmap with the text + */ + public Bitmap text2Bitmap(Context ctx, String text) { + Paint paint = new Paint(); + Typeface font = Typeface.createFromAsset(ctx.getAssets(), "fonts/Roboto-Bold.ttf"); + paint.setTypeface(font); + paint.setColor(Color.WHITE); + paint.setTextSize(48.0f); + 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; + } +} |