aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/ruesga/android/wallpapers/photophase/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/ruesga/android/wallpapers/photophase/utils')
-rw-r--r--src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java147
-rw-r--r--src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java115
-rw-r--r--src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java531
-rw-r--r--src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java119
-rw-r--r--src/com/ruesga/android/wallpapers/photophase/utils/Utils.java83
5 files changed, 995 insertions, 0 deletions
diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java b/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java
new file mode 100644
index 0000000..4fb7257
--- /dev/null
+++ b/src/com/ruesga/android/wallpapers/photophase/utils/BitmapUtils.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2013 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.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Matrix;
+import android.media.ExifInterface;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A helper class for deal with Bitmaps
+ */
+public class BitmapUtils {
+
+ /**
+ * Method that decodes a bitmap
+ *
+ * @param bitmap The bitmap buffer to decode
+ * @return Bitmap The decoded bitmap
+ */
+ public static Bitmap decodeBitmap(InputStream bitmap) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferQualityOverSpeed = false;
+ options.inPreferredConfig = Bitmap.Config.RGB_565;
+ return BitmapFactory.decodeStream(bitmap, null, options);
+ }
+
+ /**
+ * Method that decodes a bitmap
+ *
+ * @param file The bitmap file to decode
+ * @param reqWidth The request width
+ * @param reqHeight The request height
+ * @return Bitmap The decoded bitmap
+ */
+ public static Bitmap decodeBitmap(File file, int reqWidth, int reqHeight) {
+ // First decode with inJustDecodeBounds=true to check dimensions
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+
+ // Calculate inSampleSize (use 1024 as maximum size, the minimum supported
+ // by all the gles20 devices)
+ options.inSampleSize = calculateBitmapRatio(
+ options,
+ Math.min(reqWidth, 1024),
+ Math.min(reqHeight, 1024));
+
+ // Decode the bitmap with inSampleSize set
+ options.inJustDecodeBounds = false;
+ options.inPreferQualityOverSpeed = false;
+ options.inPurgeable = true;
+ options.inInputShareable = true;
+ options.inDither = true;
+ Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
+ if (bitmap == null) {
+ return null;
+ }
+
+ // Test if the bitmap has exif format, and decode properly
+ Bitmap out = decodeExifBitmap(file, bitmap);
+ if (!out.equals(bitmap)) {
+ bitmap.recycle();
+ bitmap = null;
+ }
+ return out;
+ }
+
+ /**
+ * Method that decodes an Exif bitmap
+ *
+ * @param file The file to decode
+ * @param src The bitmap reference
+ * @return Bitmap The decoded bitmap
+ */
+ private static Bitmap decodeExifBitmap(File file, Bitmap src) {
+ try {
+ // Try to load the bitmap as a bitmap file
+ ExifInterface exif = new ExifInterface(file.getAbsolutePath());
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
+ if (orientation == 0) {
+ return src;
+ }
+ Matrix matrix = new Matrix();
+ if (orientation == 6) {
+ matrix.postRotate(90);
+ } else if (orientation == 3) {
+ matrix.postRotate(180);
+ } else if (orientation == 8) {
+ matrix.postRotate(270);
+ }
+ // Rotate the bitmap
+ return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
+ } catch (IOException e) {
+ // Ignore
+ }
+ return src;
+ }
+
+ /**
+ * Method that calculate the bitmap size prior to decode
+ *
+ * @param options The bitmap factory options
+ * @param reqWidth The request width
+ * @param reqHeight The request height
+ * @return int The picture ratio
+ */
+ private static int calculateBitmapRatio(Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ // Calculate ratios of height and width to requested height and width
+ final int heightRatio = Math.round((float) height / (float) reqHeight);
+ final int widthRatio = Math.round((float) width / (float) reqWidth);
+
+ // Choose the smallest ratio as inSampleSize value, this will guarantee
+ // a final image with both dimensions larger than or equal to the
+ // requested height and width.
+ inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
+ }
+
+ return inSampleSize;
+ }
+
+}
diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java b/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java
new file mode 100644
index 0000000..9d6b383
--- /dev/null
+++ b/src/com/ruesga/android/wallpapers/photophase/utils/DispositionUtil.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 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.utils;
+
+import android.graphics.Rect;
+
+import com.ruesga.android.wallpapers.photophase.model.Disposition;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class with disposition utils
+ */
+public final class DispositionUtil {
+
+ /**
+ * Method that converts a disposition string to a disposition reference
+ *
+ * @param value The value to convert
+ * @return List<Disposition> The dispositions reference
+ */
+ public static List<Disposition> toDispositions(String value) {
+ String[] v = value.split("\\|");
+ List<Disposition> dispositions = new ArrayList<Disposition>(v.length);
+ for (String s : v) {
+ String[] s1 = s.split(":");
+ String[] s2 = s1[0].split("x");
+ String[] s3 = s1[1].split("x");
+ Disposition disposition = new Disposition();
+ disposition.x = Integer.parseInt(s2[0]);
+ disposition.y = Integer.parseInt(s2[1]);
+ disposition.w = Integer.parseInt(s3[0]) - disposition.x + 1;
+ disposition.h = Integer.parseInt(s3[1]) - disposition.y + 1;
+ dispositions.add(disposition);
+ }
+ Collections.sort(dispositions);
+ return dispositions;
+ }
+
+ /**
+ * Method that converts a disposition reference to a disposition string
+ *
+ * @param dispositions The value to convert
+ * @return String The dispositions string
+ */
+ public static String fromDispositions(List<Disposition> dispositions) {
+ Collections.sort(dispositions);
+ StringBuilder sb = new StringBuilder();
+ int count = dispositions.size();
+ for (int i = 0; i < count; i++) {
+ Disposition disposition = dispositions.get(i);
+ sb.append(disposition.x)
+ .append("x")
+ .append(disposition.y)
+ .append(":")
+ .append(disposition.x + disposition.w - 1)
+ .append("x")
+ .append(disposition.y + disposition.h - 1);
+ if (i < (count - 1)) {
+ sb.append("|");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Method that transform the disposition to a byte matrix
+ *
+ * @param dispositions The
+ * @return byte[][] The boolean matrix of the disposition
+ */
+ public static byte[][] toMatrix(List<Disposition> dispositions, int cols, int rows) {
+ byte[][] matrix = new byte[rows][cols];
+ for (Disposition disposition : dispositions) {
+ int count = disposition.y + disposition.h;
+ for (int row = disposition.y; row < count; row++) {
+ int count2 = disposition.x + disposition.w;
+ for (int col = disposition.x; col < count2; col++) {
+ matrix[row][col] = 1;
+ }
+ }
+ }
+ return matrix;
+ }
+
+ /**
+ * Method that returns a disposition from a {@link Rect} reference
+ *
+ * @return Disposition The disposition
+ */
+ public static Disposition fromRect(Rect r) {
+ Disposition disposition = new Disposition();
+ disposition.x = r.left;
+ disposition.y = r.top;
+ disposition.w = r.width();
+ disposition.h = r.height();
+ return disposition;
+ }
+}
diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java b/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java
new file mode 100644
index 0000000..51b7b74
--- /dev/null
+++ b/src/com/ruesga/android/wallpapers/photophase/utils/GLESUtil.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2013 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.utils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.media.effect.Effect;
+import android.opengl.GLES20;
+import android.opengl.GLUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+
+/**
+ * A helper class with some useful methods for deal with GLES.
+ */
+public final class GLESUtil {
+
+ private static final String TAG = "GLESUtil";
+
+ private static final boolean DEBUG = false;
+
+ private static final Object sSync = new Object();
+
+ /**
+ * A helper class to deal with OpenGL float colors.
+ */
+ public static class GLColor {
+
+ private static final float MAX_COLOR = 255.0f;
+
+ /**
+ * Red
+ */
+ public float r;
+ /**
+ * Green
+ */
+ public float g;
+ /**
+ * Blue
+ */
+ public float b;
+ /**
+ * Alpha
+ */
+ public float a;
+
+ /**
+ * Constructor of <code>GLColor</code> from ARGB
+ *
+ * @param a Alpha
+ * @param r Red
+ * @param g Green
+ * @param b Alpha
+ */
+ public GLColor(int a, int r, int g, int b) {
+ this.a = a / MAX_COLOR;
+ this.r = r / MAX_COLOR;
+ this.g = g / MAX_COLOR;
+ this.b = b / MAX_COLOR;
+ }
+
+ /**
+ * Constructor of <code>GLColor</code> from ARGB.
+ *
+ * @param argb An #AARRGGBB string
+ */
+ public GLColor(String argb) {
+ int color = Color.parseColor(argb);
+ this.a = Color.alpha(color) / MAX_COLOR;
+ this.r = Color.red(color) / MAX_COLOR;
+ this.g = Color.green(color) / MAX_COLOR;
+ this.b = Color.blue(color) / MAX_COLOR;
+ }
+
+ /**
+ * Constructor of <code>GLColor</code> from ARGB.
+ *
+ * @param argb An #AARRGGBB number
+ */
+ public GLColor(int argb) {
+ this.a = Color.alpha(argb) / MAX_COLOR;
+ this.r = Color.red(argb) / MAX_COLOR;
+ this.g = Color.green(argb) / MAX_COLOR;
+ this.b = Color.blue(argb) / MAX_COLOR;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Float.floatToIntBits(a);
+ result = prime * result + Float.floatToIntBits(b);
+ result = prime * result + Float.floatToIntBits(g);
+ result = prime * result + Float.floatToIntBits(r);
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ GLColor other = (GLColor) obj;
+ if (Float.floatToIntBits(a) != Float.floatToIntBits(other.a))
+ return false;
+ if (Float.floatToIntBits(b) != Float.floatToIntBits(other.b))
+ return false;
+ if (Float.floatToIntBits(g) != Float.floatToIntBits(other.g))
+ return false;
+ if (Float.floatToIntBits(r) != Float.floatToIntBits(other.r))
+ return false;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "#"+Integer.toHexString(Color.argb((int)a, (int)r, (int)g, (int)b));
+ }
+ }
+
+ /**
+ * Class that holds some information about a GLES texture
+ */
+ public static class GLESTextureInfo {
+ /**
+ * Handle of the texture
+ */
+ public int handle = 0;
+ /**
+ * The bitmap reference
+ */
+ public Bitmap bitmap;
+ /**
+ * The path to the texture
+ */
+ public File path;
+ /**
+ * The effect to apply
+ */
+ public Effect effect;
+ }
+
+ /**
+ * Method that load a vertex shader and returns its handler identifier.
+ *
+ * @param src The source shader
+ * @return int The handler identifier of the shader
+ */
+ public static int loadVertexShader(String src) {
+ return loadShader(src, GLES20.GL_VERTEX_SHADER);
+ }
+
+ /**
+ * Method that load a fragment shader and returns its handler identifier.
+ *
+ * @param src The source shader
+ * @return int The handler identifier of the shader
+ */
+ public static int loadFragmentShader(String src) {
+ return loadShader(src, GLES20.GL_FRAGMENT_SHADER);
+ }
+
+ /**
+ * Method that load a shader and returns its handler identifier.
+ *
+ * @param src The source shader
+ * @param type The type of shader
+ * @return int The handler identifier of the shader
+ */
+ public static int loadShader(String src, int type) {
+ int[] compiled = new int[1];
+ // Create, load and compile the shader
+ int shader = GLES20.glCreateShader(type);
+ GLESUtil.glesCheckError("glCreateShader");
+ if (shader <= 0) {
+ Log.e(TAG, "Cannot create a shader");
+ return 0;
+ }
+ GLES20.glShaderSource(shader, src);
+ GLESUtil.glesCheckError("glShaderSource");
+ GLES20.glCompileShader(shader);
+ GLESUtil.glesCheckError("glesCheckError");
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ GLESUtil.glesCheckError("glesCheckError");
+ if (compiled[0] <= 0) {
+ String msg = "Shader compilation error trace:\n" + GLES20.glGetShaderInfoLog(shader);
+ Log.e(TAG, msg);
+ return 0;
+ }
+ return shader;
+ }
+
+ /**
+ * Method that create a new program from its shaders (vertex and fragment)
+ *
+ * @param res A resources reference
+ * @param vertexShaderId The vertex shader glsl resource
+ * @param fragmentShaderId The fragment shader glsl resource
+ * @return int The handler identifier of the program
+ */
+ public static int createProgram(Resources res, int vertexShaderId, int fragmentShaderId) {
+ return createProgram(
+ readResource(res, vertexShaderId),
+ readResource(res, fragmentShaderId));
+ }
+
+ /**
+ * Method that create a new program from its shaders (vertex and fragment)
+ *
+ * @param vertexShaderSrc The vertex shader
+ * @param fragmentShaderSrc The fragment shader
+ * @return int The handler identifier of the program.
+ */
+ public static int createProgram(String vertexShaderSrc, String fragmentShaderSrc) {
+ int vshader = 0;
+ int fshader = 0;
+ int progid = 0;
+ int[] link = new int[1];
+
+ try {
+ // Check that we have valid shaders
+ if (vertexShaderSrc == null || fragmentShaderSrc == null) {
+ return 0;
+ }
+
+ // Load the vertex and fragment shaders
+ vshader = loadVertexShader(vertexShaderSrc);
+ fshader = loadFragmentShader(fragmentShaderSrc);
+
+ // Create the programa ref
+ progid = GLES20.glCreateProgram();
+ GLESUtil.glesCheckError("glCreateProgram");
+ if (progid <= 0) {
+ String msg = "Cannot create a program";
+ Log.e(TAG, msg);
+ return 0;
+ }
+
+ // Attach the shaders
+ GLES20.glAttachShader(progid, vshader);
+ GLESUtil.glesCheckError("glAttachShader");
+ GLES20.glAttachShader(progid, fshader);
+ GLESUtil.glesCheckError("glAttachShader");
+
+ // Link the program
+ GLES20.glLinkProgram(progid);
+ GLESUtil.glesCheckError("glLinkProgram");
+
+ GLES20.glGetProgramiv(progid, GLES20.GL_LINK_STATUS, link, 0);
+ GLESUtil.glesCheckError("glGetProgramiv");
+ if (link[0] <= 0) {
+ String msg = "Program compilation error trace:\n" + GLES20.glGetProgramInfoLog(progid);
+ Log.e(TAG, msg);
+ return 0;
+ }
+
+ // Return the program
+ return progid;
+
+ } finally {
+ // Delete the shaders
+ if (vshader != 0) {
+ GLES20.glDeleteShader(vshader);
+ GLESUtil.glesCheckError("glDeleteShader");
+ }
+ if (fshader != 0) {
+ GLES20.glDeleteShader(fshader);
+ GLESUtil.glesCheckError("glDeleteShader");
+ }
+ }
+ }
+
+ /**
+ * Method that loads a texture from a file.
+ *
+ * @param file The image file
+ * @param dimensions The desired dimensions
+ * @param effect The effect to apply to the image or null if no effect is needed
+ * @param dimen The new dimensions
+ * @param recycle If the bitmap should be recycled
+ * @return GLESTextureInfo The texture info
+ */
+ public static GLESTextureInfo loadTexture(
+ File file, Rect dimensions, Effect effect, Rect dimen, boolean recycle) {
+ Bitmap bitmap = null;
+ try {
+ // Decode and associate the bitmap (invert the desired dimensions)
+ bitmap = BitmapUtils.decodeBitmap(file, dimensions.height(), dimensions.width());
+ if (bitmap == null) {
+ Log.e(TAG, "Failed to decode the file bitmap");
+ return new GLESTextureInfo();
+ }
+
+ if (DEBUG) Log.d(TAG, "image: " + file.getAbsolutePath());
+ GLESTextureInfo ti = loadTexture(bitmap, effect, dimen);
+ ti.path = file;
+ return ti;
+
+ } catch (Exception e) {
+ String msg = "Failed to generate a valid texture from file: " + file.getAbsolutePath();
+ Log.e(TAG, msg, e);
+ return new GLESTextureInfo();
+
+ } finally {
+ // Recycle the bitmap
+ if (bitmap != null && recycle) {
+ bitmap.recycle();
+ bitmap = null;
+ }
+ }
+ }
+
+ /**
+ * Method that loads a texture from a resource context.
+ *
+ * @param ctx The current context
+ * @param resourceId The resource identifier
+ * @param effect The effect to apply to the image or null if no effect is needed
+ * @param dimen The new dimensions
+ * @param recycle If the bitmap should be recycled
+ * @return GLESTextureInfo The texture info
+ */
+ public static GLESTextureInfo loadTexture(
+ Context ctx, int resourceId, Effect effect, Rect dimen, boolean recycle) {
+ Bitmap bitmap = null;
+ InputStream raw = null;
+ try {
+ // Decode and associate the bitmap
+ raw = ctx.getResources().openRawResource(resourceId);
+ bitmap = BitmapUtils.decodeBitmap(raw);
+ if (bitmap == null) {
+ String msg = "Failed to decode the resource bitmap";
+ Log.e(TAG, msg);
+ return new GLESTextureInfo();
+ }
+
+ if (DEBUG) Log.d(TAG, "resourceId: " + resourceId);
+ GLESTextureInfo ti = loadTexture(bitmap, effect, dimen);
+ return ti;
+
+ } catch (Exception e) {
+ String msg = "Failed to generate a valid texture from resource: " + resourceId;
+ Log.e(TAG, msg, e);
+ return new GLESTextureInfo();
+
+ } finally {
+ // Close the buffer
+ try {
+ if (raw != null) {
+ raw.close();
+ }
+ } catch (IOException e) {
+ // Ignore.
+ }
+ // Recycle the bitmap
+ if (bitmap != null && recycle) {
+ bitmap.recycle();
+ bitmap = null;
+ }
+ }
+ }
+
+ /**
+ * Method that loads texture from a bitmap reference.
+ *
+ * @param bitmap The bitmap reference
+ * @param effect The effect to apply to the image or null if no effect is needed
+ * @param dimen The new dimensions
+ * @return GLESTextureInfo The texture info
+ */
+ public static GLESTextureInfo loadTexture(Bitmap bitmap, Effect effect, Rect dimen) {
+ // Check that we have a valid image name reference
+ if (bitmap == null) {
+ return new GLESTextureInfo();
+ }
+
+ int num = effect == null ? 1 : 2;
+
+ int[] textureHandles = new int[num];
+ GLES20.glGenTextures(num, textureHandles, 0);
+ GLESUtil.glesCheckError("glGenTextures");
+ if (textureHandles[0] <= 0 || (effect != null && textureHandles[1] <= 0)) {
+ Log.e(TAG, "Failed to generate a valid texture");
+ return new GLESTextureInfo();
+ }
+
+ // Bind the texture to the name
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandles[0]);
+ GLESUtil.glesCheckError("glBindTexture");
+
+ // Set the texture properties
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
+ GLESUtil.glesCheckError("glTexParameteri");
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
+ GLESUtil.glesCheckError("glTexParameteri");
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLESUtil.glesCheckError("glTexParameteri");
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GLESUtil.glesCheckError("glTexParameteri");
+
+ // Load the texture
+ GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
+ if (!GLES20.glIsTexture(textureHandles[0])) {
+ Log.e(TAG, "Failed to load a valid texture");
+ return new GLESTextureInfo();
+ }
+
+ // Has a effect?
+ int handle = textureHandles[0];
+ if (effect != null) {
+ // Apply the effect (we need a thread-safe call here)
+ synchronized (sSync) {
+ // No more than 1024 (the minimum supported by all the gles20 devices)
+ int w = Math.min(dimen.width(), 1024);
+ int h = Math.min(dimen.width(), 1024);
+ effect.apply(textureHandles[0], w, h, textureHandles[1]);
+ }
+ handle = textureHandles[1];
+
+ // Delete the unused texture
+ int[] textures = {textureHandles[0]};
+ GLES20.glDeleteTextures(1, textures, 0);
+ GLESUtil.glesCheckError("glDeleteTextures");
+ }
+
+ // Return the texture handle identifier and the associated info
+ GLESTextureInfo ti = new GLESTextureInfo();
+ ti.handle = handle;
+ ti.bitmap = bitmap;
+ ti.path = null;
+ return ti;
+ }
+
+ /**
+ * Method that checks if an GLES error is present
+ *
+ * @param func The GLES function to check
+ * @return boolean If there was an error
+ */
+ public static boolean glesCheckError(String func) {
+ int error = GLES20.glGetError();
+ if (error != 0) {
+ Log.e(TAG, "GLES20 Error (" + glesGetErrorModule() + ") (" + func + "): " +
+ GLUtils.getEGLErrorString(error));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Method that returns the line and module that generates the current error
+ *
+ * @return String The line and module
+ */
+ private static String glesGetErrorModule() {
+ try {
+ return String.valueOf(Thread.currentThread().getStackTrace()[4]);
+ } catch (IndexOutOfBoundsException ioobEx) {
+ // Ignore
+ }
+ return "";
+ }
+
+ /**
+ * Method that read a resource.
+ *
+ * @param res The resources reference
+ * @param resId The resource identifier
+ * @return String The shader source
+ * @throws IOException If an error occurs while loading the resource
+ */
+ private static String readResource(Resources res, int resId) {
+ Reader reader = new InputStreamReader(res.openRawResource(resId));
+ try {
+ final int BUFFER = 1024;
+ char[] data = new char[BUFFER];
+ int read = 0;
+ StringBuilder sb = new StringBuilder();
+ while ((read = reader.read(data, 0, BUFFER)) != -1) {
+ sb.append(data, 0, read);
+ }
+ return sb.toString();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to read the resource " + resId);
+ return null;
+ } finally {
+ try {
+ reader.close();
+ } catch (Exception ex) {
+ // Ignore
+ }
+ }
+ }
+
+}
diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java b/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java
new file mode 100644
index 0000000..c7009c3
--- /dev/null
+++ b/src/com/ruesga/android/wallpapers/photophase/utils/MERAlgorithm.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 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.utils;
+
+import android.graphics.Rect;
+import java.util.Stack;
+
+/**
+ * The maximal empty rectangle algorithm that allows to find the rectangle with the maximal
+ * area that could be create in empty areas (in this case 0 in a byte matrix)
+ */
+//
+// Based on the source discussed at http://discuss.leetcode.com/questions/260/maximal-rectangle
+//
+public final class MERAlgorithm {
+
+ /**
+ * Method that returns the maximal empty rectangle (MER) for a matrix of bytes (1/0)
+ *
+ * @param matrix The matrix
+ * @return Rect The maximal empty rectangle
+ */
+ public static Rect getMaximalEmptyRectangle(byte[][] matrix) {
+ // Check matrix
+ int rows = matrix.length;
+ if (rows == 0) return null;
+
+ // Convert to histogram
+ int[][] histogram = toHistogram(matrix);
+
+ // Find the maximal area of every histogram
+ Rect maxRect = new Rect();
+ for (int i = 0; i < rows; ++i) {
+ Rect rect = maximalRectangle(histogram[i], i);
+ if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) {
+ maxRect = rect;
+ }
+ }
+ return maxRect;
+ }
+
+ /**
+ * Method that returns the maximal rectangle for an histogram of areas
+ *
+ * @return Rect The maximal rectangle histogram/area
+ */
+ @SuppressWarnings("boxing")
+ private static Rect maximalRectangle(int[] histogram, int row) {
+ Stack<Integer> stack = new Stack<Integer>();
+ int length = histogram.length;
+ Rect maxRect = new Rect();
+ int i = 0;
+ while (i < length) {
+ if (stack.isEmpty() || histogram[i] >= histogram[stack.peek()]) {
+ stack.push(i++);
+ } else {
+ Rect rect = new Rect();
+ rect.left = stack.pop();
+ rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1));
+ rect.top = row - histogram[rect.left] + 1;
+ rect.bottom = rect.top + histogram[rect.left];
+ if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) {
+ maxRect = rect;
+ }
+ }
+ }
+ while (!stack.isEmpty()) {
+ Rect rect = new Rect();
+ rect.left = stack.pop();
+ rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1));
+ rect.top = row - histogram[rect.left] + 1;
+ rect.bottom = rect.top + histogram[rect.left];
+ if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) {
+ maxRect = rect;
+ }
+ }
+ return maxRect;
+ }
+
+ /**
+ * Method that converts the empty areas to a histogram
+ *
+ * @param matrix The matrix where to find the MER
+ * return int[][] The histogram of empty areas
+ */
+ private static int[][] toHistogram(byte[][] matrix) {
+ int rows = matrix.length;
+ int cols = matrix[0].length;
+ int[][] histogram = new int[rows][cols];
+ for (int h=0; h < cols; h++) {
+ if (matrix[0][h] == 0) {
+ histogram[0][h] = 1;
+ }
+ }
+ for (int w=1; w < rows; w++) {
+ for (int h=0; h < cols; h++) {
+ if (matrix[w][h] == 1) {
+ continue;
+ }
+ histogram[w][h] = histogram[w-1][h] + 1;
+ }
+ }
+ return histogram;
+ }
+}
diff --git a/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java b/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java
new file mode 100644
index 0000000..296b96a
--- /dev/null
+++ b/src/com/ruesga/android/wallpapers/photophase/utils/Utils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2013 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.utils;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.RectF;
+import android.util.DisplayMetrics;
+
+import java.util.Random;
+
+/**
+ * A helper class with utilities
+ */
+public class Utils {
+
+ private static Random sRandom = new Random();
+
+ /**
+ * This method converts dp unit to equivalent device specific value in pixels.
+ *
+ * @param ctx The current context
+ * @param dp A value in dp (Device independent pixels) unit
+ * @return float A float value to represent Pixels equivalent to dp according to device
+ */
+ public static float convertDpToPixel(Context ctx, float dp) {
+ Resources resources = ctx.getResources();
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+ return dp * (metrics.densityDpi / 160f);
+ }
+
+ /**
+ * Used to determine if the device is a tablet or not
+ *
+ * @param context The {@link Context} to use.
+ * @return True if the device is a tablet, false otherwise.
+ */
+ public static final boolean isTablet(final Context context) {
+ final int layout = context.getResources().getConfiguration().screenLayout;
+ return (layout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
+ }
+
+ /**
+ * Method that converts a rect from a vertex data
+ *
+ * @param vertex The vertex array
+ * @return RectF The rect data
+ */
+ public static RectF rectFromVertex(float[] vertex) {
+ RectF rect = new RectF();
+ rect.left = vertex[0];
+ rect.top = vertex[7];
+ rect.right = vertex[6];
+ rect.bottom = vertex[1];
+ return rect;
+ }
+
+ /**
+ * Method that returns a random number between two numbers.
+ *
+ * @param low The low number
+ * @param high The high number
+ * @return int The random number
+ */
+ public static int getNextRandom(int low, int high) {
+ return low + (int)(sRandom.nextDouble() * ((high - low) + 1));
+ }
+} \ No newline at end of file