* Wrapping is typically used for debugging purposes. *
* The default value is null. * @param glWrapper the new GLWrapper */ public void setGLWrapper(GLWrapper glWrapper) { mGLWrapper = glWrapper; } /** * Set the debug flags to a new value. The value is * constructed by OR-together zero or more * of the DEBUG_CHECK_* constants. The debug flags take effect * whenever a surface is created. The default value is zero. * @param debugFlags the new debug flags * @see #DEBUG_CHECK_GL_ERROR * @see #DEBUG_LOG_GL_CALLS */ public void setDebugFlags(int debugFlags) { mDebugFlags = debugFlags; } /** * Get the current value of the debug flags. * @return the current value of the debug flags. */ public int getDebugFlags() { return mDebugFlags; } /** * Control whether the EGL context is preserved when the GLSurfaceView is paused and * resumed. *
* If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. * Whether the EGL context is actually preserved or not depends upon whether the * Android device that the program is running on can support an arbitrary number of EGL * contexts or not. Devices that can only support a limited number of EGL contexts must * release the EGL context in order to allow multiple applications to share the GPU. *
* If set to false, the EGL context will be released when the GLSurfaceView is paused, * and recreated when the GLSurfaceView is resumed. *
* * The default is false. * * @param preserveOnPause preserve the EGL context when paused */ public void setPreserveEGLContextOnPause(boolean preserveOnPause) { mPreserveEGLContextOnPause = preserveOnPause; } /** * @return true if the EGL context will be preserved when paused */ public boolean getPreserveEGLContextOnPause() { return mPreserveEGLContextOnPause; } /** * Set the renderer associated with this view. Also starts the thread that * will call the renderer, which in turn causes the rendering to start. *
This method should be called once and only once in the life-cycle of * a GLSurfaceView. *
The following GLSurfaceView methods can only be called before * setRenderer is called: *
* The following GLSurfaceView methods can only be called after * setRenderer is called: *
If this method is * called, it must be called before {@link #setRenderer(Renderer)} * is called. *
* If this method is not called, then by default * a context will be created with no shared context and * with a null attribute list. */ public void setEGLContextFactory(EGLContextFactory factory) { checkRenderThreadState(); mEGLContextFactory = factory; } /** * Install a custom EGLWindowSurfaceFactory. *
If this method is * called, it must be called before {@link #setRenderer(Renderer)} * is called. *
* If this method is not called, then by default * a window surface will be created with a null attribute list. */ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { checkRenderThreadState(); mEGLWindowSurfaceFactory = factory; } /** * Install a custom EGLConfigChooser. *
If this method is * called, it must be called before {@link #setRenderer(Renderer)} * is called. *
* If no setEGLConfigChooser method is called, then by default the * view will choose an EGLConfig that is compatible with the current * android.view.Surface, with a depth buffer depth of * at least 16 bits. * @param configChooser */ public void setEGLConfigChooser(EGLConfigChooser configChooser) { checkRenderThreadState(); mEGLConfigChooser = configChooser; } /** * Install a config chooser which will choose a config * as close to 16-bit RGB as possible, with or without an optional depth * buffer as close to 16-bits as possible. *
If this method is * called, it must be called before {@link #setRenderer(Renderer)} * is called. *
* If no setEGLConfigChooser method is called, then by default the * view will choose an RGB_888 surface with a depth buffer depth of * at least 16 bits. * * @param needDepth */ public void setEGLConfigChooser(boolean needDepth) { setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); } /** * Install a config chooser which will choose a config * with at least the specified depthSize and stencilSize, * and exactly the specified redSize, greenSize, blueSize and alphaSize. *
If this method is * called, it must be called before {@link #setRenderer(Renderer)} * is called. *
* If no setEGLConfigChooser method is called, then by default the * view will choose an RGB_888 surface with a depth buffer depth of * at least 16 bits. * */ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) { setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize)); } /** * Inform the default EGLContextFactory and default EGLConfigChooser * which EGLContext client version to pick. *
Use this method to create an OpenGL ES 2.0-compatible context. * Example: *
* public MyView(Context context) {
* super(context);
* setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
* setRenderer(new MyRenderer());
* }
*
* Note: Activities which require OpenGL ES 2.0 should indicate this by * setting @lt;uses-feature android:glEsVersion="0x00020000" in the activity's * AndroidManifest.xml file. *
If this method is called, it must be called before {@link #setRenderer(Renderer)} * is called. *
This method only affects the behavior of the default EGLContexFactory and the * default EGLConfigChooser. If * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context. * If * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config. * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0 */ public void setEGLContextClientVersion(int version) { checkRenderThreadState(); mEGLContextClientVersion = version; } /** * Set the rendering mode. When renderMode is * RENDERMODE_CONTINUOUSLY, the renderer is called * repeatedly to re-render the scene. When renderMode * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. *
* Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance * by allowing the GPU and CPU to idle when the view does not need to be updated. *
* This method can only be called after {@link #setRenderer(Renderer)} * * @param renderMode one of the RENDERMODE_X constants * @see #RENDERMODE_CONTINUOUSLY * @see #RENDERMODE_WHEN_DIRTY */ public void setRenderMode(int renderMode) { mGLThread.setRenderMode(renderMode); } /** * Get the current rendering mode. May be called * from any thread. Must not be called before a renderer has been set. * @return the current rendering mode. * @see #RENDERMODE_CONTINUOUSLY * @see #RENDERMODE_WHEN_DIRTY */ public int getRenderMode() { return mGLThread.getRenderMode(); } /** * Request that the renderer render a frame. * This method is typically used when the render mode has been set to * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. * May be called * from any thread. Must not be called before a renderer has been set. */ public void requestRender() { mGLThread.requestRender(); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { mGLThread.surfaceCreated(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { mGLThread.onWindowResize(width, height); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mGLThread.onWindowResize(w, h); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { mGLThread.surfaceDestroyed(); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { requestRender(); } /** * This method is part of the SurfaceHolder.Callback interface, and is * not normally called or subclassed by clients of GLSurfaceView. */ public void on(SurfaceHolder holder) { mGLThread.surfaceCreated(); } /** * Inform the view that the activity is paused. The owner of this view must * call this method when the activity is paused. Calling this method will * pause the rendering thread. * Must not be called before a renderer has been set. */ public void onPause() { mGLThread.onPause(); } /** * Inform the view that the activity is resumed. The owner of this view must * call this method when the activity is resumed. Calling this method will * recreate the OpenGL display and resume the rendering * thread. * Must not be called before a renderer has been set. */ public void onResume() { mGLThread.onResume(); } /** * Queue a runnable to be run on the GL rendering thread. This can be used * to communicate with the Renderer on the rendering thread. * Must not be called before a renderer has been set. * @param r the runnable to be run on the GL rendering thread. */ public void queueEvent(Runnable r) { mGLThread.queueEvent(r); } /** * This method is used as part of the View class and is not normally * called or subclassed by clients of GLSurfaceView. */ @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (LOG_ATTACH_DETACH) { Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); } if (mDetached && (mRenderer != null)) { int renderMode = RENDERMODE_CONTINUOUSLY; if (mGLThread != null) { renderMode = mGLThread.getRenderMode(); } mGLThread = new GLThread(mThisWeakRef); if (renderMode != RENDERMODE_CONTINUOUSLY) { mGLThread.setRenderMode(renderMode); } mGLThread.start(); } mDetached = false; } /** * This method is used as part of the View class and is not normally * called or subclassed by clients of GLSurfaceView. * Must not be called before a renderer has been set. */ @Override protected void onDetachedFromWindow() { if (LOG_ATTACH_DETACH) { Log.d(TAG, "onDetachedFromWindow"); } if (mGLThread != null) { mGLThread.requestExitAndWait(); } mDetached = true; super.onDetachedFromWindow(); } // ---------------------------------------------------------------------- /** * An interface used to wrap a GL interface. *
Typically * used for implementing debugging and tracing on top of the default * GL interface. You would typically use this by creating your own class * that implemented all the GL methods by delegating to another GL instance. * Then you could add your own behavior before or after calling the * delegate. All the GLWrapper would do was instantiate and return the * wrapper GL instance: *
* class MyGLWrapper implements GLWrapper {
* GL wrap(GL gl) {
* return new MyGLImplementation(gl);
* }
* static class MyGLImplementation implements GL,GL10,GL11,... {
* ...
* }
* }
*
* @see #setGLWrapper(GLWrapper)
*/
public interface GLWrapper {
/**
* Wraps a gl interface in another gl interface.
* @param gl a GL interface that is to be wrapped.
* @return either the input argument or another GL object that wraps the input argument.
*/
GL wrap(GL gl);
}
/**
* An interface for customizing the eglCreateContext and eglDestroyContext calls.
* * This interface must be implemented by clients wishing to call * */ public interface EGLContextFactory { EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); } private class DefaultContextFactory implements EGLContextFactory { private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, EGL10.EGL_NONE }; return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, mEGLContextClientVersion != 0 ? attrib_list : null); } public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { if (!egl.eglDestroyContext(display, context)) { Log.e("DefaultContextFactory", "display:" + display + " context: " + context); if (LOG_THREADS) { Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId()); } EglHelper.throwEglException("eglDestroyContex", egl.eglGetError()); } } } /** * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. *
* This interface must be implemented by clients wishing to call * */ public interface EGLWindowSurfaceFactory { /** * @return null if the surface cannot be constructed. */ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow); void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); } private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) { EGLSurface result = null; try { result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); } catch (IllegalArgumentException e) { // This exception indicates that the surface flinger surface // is not valid. This can happen if the surface flinger surface has // been torn down, but the application has not yet been // notified via SurfaceHolder.Callback.surfaceDestroyed. // In theory the application should be notified first, // but in practice sometimes it is not. See b/4588890 Log.e(TAG, "eglCreateWindowSurface", e); } return result; } public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { egl.eglDestroySurface(display, surface); } } /** * An interface for choosing an EGLConfig configuration from a list of * potential configurations. *
* This interface must be implemented by clients wishing to call
*
*/
public interface EGLConfigChooser {
/**
* Choose a configuration from the list. Implementors typically
* implement this method by calling
* {@link javax.microedition.khronos.egl.EGL10#eglChooseConfig} and iterating through the results. Please consult the
* EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
* @param egl the EGL10 for the current display.
* @param display the current display.
* @return the chosen configuration.
*/
EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}
private abstract class BaseConfigChooser
implements EGLConfigChooser {
public BaseConfigChooser(int[] configSpec) {
mConfigSpec = filterConfigSpec(configSpec);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] num_config = new int[1];
if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
num_config)) {
throw new IllegalArgumentException("eglChooseConfig failed");
}
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException(
"No configs match configSpec");
}
EGLConfig[] configs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
num_config)) {
throw new IllegalArgumentException("eglChooseConfig#2 failed");
}
EGLConfig config = chooseConfig(egl, display, configs);
if (config == null) {
throw new IllegalArgumentException("No config chosen");
}
return config;
}
abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs);
protected int[] mConfigSpec;
private int[] filterConfigSpec(int[] configSpec) {
if (mEGLContextClientVersion != 2) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
* And we know the configSpec is well formed.
*/
int len = configSpec.length;
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
}
/**
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
private class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
EGL10.EGL_BLUE_SIZE, blueSize,
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
EGL10.EGL_NONE});
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
mBlueSize = blueSize;
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
}
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
for (EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config,
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
}
}
}
return null;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
private int[] mValue;
// Subclasses can adjust these values:
protected int mRedSize;
protected int mGreenSize;
protected int mBlueSize;
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
}
/**
* This class will choose a RGB_888 surface with
* or without a depth buffer.
*
*/
private class SimpleEGLConfigChooser extends ComponentSizeChooser {
public SimpleEGLConfigChooser(boolean withDepthBuffer) {
super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
}
}
/**
* An EGL helper class.
*/
private static class EglHelper {
public EglHelper(WeakReferenceinBitmap is given, a sub-bitmap might be returned.
*/
public synchronized Bitmap decodeRegion(Rect rect, Options options) {
int unsampledInBitmapWidth = -1;
int unsampledInBitmapHeight = -1;
int sampleSize = Math.max(1, options != null ? options.inSampleSize : 1);
if (options != null && options.inBitmap != null) {
unsampledInBitmapWidth = options.inBitmap.getWidth() * sampleSize;
unsampledInBitmapHeight = options.inBitmap.getHeight() * sampleSize;
}
// Decode with rotation
switch (mRotation) {
case 90:
mTempRect.set(
rect.top, mOriginalHeight - rect.right,
rect.bottom, mOriginalHeight - rect.left);
break;
case 180:
mTempRect.set(
mOriginalWidth - rect.right, mOriginalHeight - rect.bottom,
mOriginalWidth - rect.left, mOriginalHeight - rect.top);
break;
case 270:
mTempRect.set(
mOriginalWidth - rect.bottom, rect.left,
mOriginalWidth - rect.top, rect.right);
break;
default:
mTempRect.set(rect);
}
Bitmap bitmap = mBitmapRegionDecoder.decodeRegion(mTempRect, options);
if (options != null && options.inBitmap != null &&
((mTempRect.width() != unsampledInBitmapWidth
|| mTempRect.height() != unsampledInBitmapHeight))) {
// Need to extract the sub-bitmap
Bitmap subBitmap = Bitmap.createBitmap(
bitmap, 0, 0,
mTempRect.width() / sampleSize,
mTempRect.height() / sampleSize);
if (bitmap != options.inBitmap) {
bitmap.recycle();
}
bitmap = subBitmap;
}
if (mRotateMatrix != null) {
// Rotate decoded bitmap
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(),
mRotateMatrix, true);
if (options == null || bitmap != options.inBitmap) {
bitmap.recycle();
}
bitmap = rotatedBitmap;
}
return bitmap;
}
public synchronized int getWidth() {
return (mRotation == 90 || mRotation == 270) ? mOriginalHeight : mOriginalWidth;
}
public synchronized int getHeight() {
return (mRotation == 90 || mRotation == 270) ? mOriginalWidth : mOriginalHeight;
}
public synchronized void destroy() {
mBitmapRegionDecoder.recycle();
mBitmapRegionDecoder = null;
try {
mInputStream.close();
} catch (IOException ignored) {
ignored.printStackTrace();
}
}
}
================================================
FILE: library/src/main/java/com/roger/missview/library/util/ImageBlurrer.java
================================================
/*
* Copyright 2014 Google Inc.
*
* 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.roger.missview.library.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.Matrix3f;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.renderscript.ScriptIntrinsicColorMatrix;
public class ImageBlurrer {
public static final int MAX_SUPPORTED_BLUR_PIXELS = 25;
private RenderScript mRS;
private ScriptIntrinsicBlur mSIBlur;
private ScriptIntrinsicColorMatrix mSIGrey;
private Allocation mTmp1;
private Allocation mTmp2;
public ImageBlurrer(Context context) {
mRS = RenderScript.create(context);
mSIBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
mSIGrey = ScriptIntrinsicColorMatrix.create(mRS);
}
public Bitmap blurBitmap(Bitmap src, float radius, float desaturateAmount) {
Bitmap dest = Bitmap.createBitmap(src);
if ((int) radius == 0) {
return dest;
}
if (mTmp1 != null) {
mTmp1.destroy();
}
if (mTmp2 != null) {
mTmp2.destroy();
}
mTmp1 = Allocation.createFromBitmap(mRS, src);
mTmp2 = Allocation.createFromBitmap(mRS, dest);
mSIBlur.setRadius((int) radius);
mSIBlur.setInput(mTmp1);
mSIBlur.forEach(mTmp2);
if (desaturateAmount > 0) {
desaturateAmount = MathUtil.constrain(0, 1, desaturateAmount);
Matrix3f m = new Matrix3f(new float[]{
MathUtil.interpolate(1, 0.299f, desaturateAmount),
MathUtil.interpolate(0, 0.299f, desaturateAmount),
MathUtil.interpolate(0, 0.299f, desaturateAmount),
MathUtil.interpolate(0, 0.587f, desaturateAmount),
MathUtil.interpolate(1, 0.587f, desaturateAmount),
MathUtil.interpolate(0, 0.587f, desaturateAmount),
MathUtil.interpolate(0, 0.114f, desaturateAmount),
MathUtil.interpolate(0, 0.114f, desaturateAmount),
MathUtil.interpolate(1, 0.114f, desaturateAmount),
});
mSIGrey.setColorMatrix(m);
mSIGrey.forEach(mTmp2, mTmp1);
mTmp1.copyTo(dest);
} else {
mTmp2.copyTo(dest);
}
return dest;
}
public void destroy() {
mSIBlur.destroy();
if (mTmp1 != null) {
mTmp1.destroy();
}
if (mTmp2 != null) {
mTmp2.destroy();
}
mRS.destroy();
}
}
================================================
FILE: library/src/main/java/com/roger/missview/library/util/ImageUtil.java
================================================
/*
* Copyright 2014 Google Inc.
*
* 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.roger.missview.library.util;
import android.graphics.Bitmap;
import android.graphics.Color;
public class ImageUtil {
// Make sure input images are very small!
public static float calculateDarkness(Bitmap bitmap) {
if (bitmap == null) {
return 0;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int totalLum = 0;
int n = 0;
int x, y, color;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
++n;
color = bitmap.getPixel(x, y);
totalLum += (0.21f * Color.red(color)
+ 0.71f * Color.green(color)
+ 0.07f * Color.blue(color));
}
}
return (totalLum / n) / 256f;
}
private ImageUtil() {
}
public static int calculateSampleSize(int rawHeight, int targetHeight) {
int sampleSize = 1;
while (rawHeight / (sampleSize << 1) > targetHeight) {
sampleSize <<= 1;
}
return sampleSize;
}
}
================================================
FILE: library/src/main/java/com/roger/missview/library/util/MathUtil.java
================================================
/*
* Copyright 2014 Google Inc.
*
* 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.roger.missview.library.util;
import android.util.FloatMath;
public class MathUtil {
public static float constrain(float min, float max, float v) {
return Math.max(min, Math.min(max, v));
}
public static float interpolate(float x1, float x2, float f) {
return x1 + (x2 - x1) * f;
}
public static float uninterpolate(float x1, float x2, float v) {
if (x2 - x1 == 0) {
throw new IllegalArgumentException("Can't reverse interpolate with domain size of 0");
}
return (v - x1) / (x2 - x1);
}
public static float dist(float x, float y) {
return FloatMath.sqrt(x * x + y * y);
}
public static int floorEven(int num) {
return num & ~0x01;
}
public static int roundMult4(int num) {
return (num + 2) & ~0x03;
}
public static boolean isEven(int num) {
return num % 2 == 0;
}
private MathUtil() {
}
}
================================================
FILE: library/src/main/java/com/roger/missview/library/util/TickingFloatAnimator.java
================================================
/*
* Copyright 2014 Google Inc.
*
* 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.roger.missview.library.util;
import android.animation.TimeInterpolator;
import android.os.SystemClock;
import android.view.animation.AccelerateDecelerateInterpolator;
// Non thread-safe
public class TickingFloatAnimator {
private float mStartValue = 0;
private float mCurrentValue;
private float mEndValue;
private boolean mRunning = false;
private long mStartTime;
private int mDuration = 1000;
private Runnable mEndCallback;
private TimeInterpolator mInterpolator = new AccelerateDecelerateInterpolator();
public static TickingFloatAnimator create() {
return new TickingFloatAnimator();
}
public TickingFloatAnimator from(float startValue) {
cancel();
mStartValue = startValue;
mCurrentValue = startValue;
return this;
}
public TickingFloatAnimator to(float endValue) {
mEndValue = endValue;
return this;
}
public TickingFloatAnimator withDuration(int duration) {
mDuration = Math.max(0, duration);
return this;
}
public TickingFloatAnimator withInterpolator(TimeInterpolator interpolator) {
mInterpolator = interpolator;
return this;
}
public TickingFloatAnimator withEndListener(Runnable listener) {
mEndCallback = listener;
return this;
}
public void cancel() {
mRunning = false;
mEndValue = mCurrentValue;
}
public boolean tick() {
if (!mRunning) {
return false;
}
float t;
if (mDuration <= 0) {
t = 1;
} else {
t = (float) (SystemClock.elapsedRealtime() - mStartTime) * 1f / mDuration;
if (t >= 1) {
t = 1;
}
}
if (t >= 1) {
// Ended
mRunning = false;
mCurrentValue = mEndValue;
if (mEndCallback != null) {
mEndCallback.run();
}
return false;
}
// Still running; compute value
mCurrentValue = mStartValue + mInterpolator.getInterpolation(t) * (mEndValue - mStartValue);
mRunning = true;
return true;
}
public void start() {
mRunning = true;
mStartValue = mCurrentValue;
mStartTime = SystemClock.elapsedRealtime();
tick();
}
public boolean isRunning() {
return mRunning;
}
public float currentValue() {
return mCurrentValue;
}
private TickingFloatAnimator() {
}
}
================================================
FILE: library/src/main/java/com/roger/missview/library/util/UriUtil.java
================================================
package com.roger.missview.library.util;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
/**
* Created by Administrator on 2015/4/28.
*/
public class UriUtil {
/**
* 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换
* @param context
* @param imageUri
*/
@TargetApi(19)
public static String getImageAbsolutePath(Context context, Uri imageUri) {
if (context == null || imageUri == null)
return null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {
if (isExternalStorageDocument(imageUri)) {
String docId = DocumentsContract.getDocumentId(imageUri);
String[] split = docId.split(":");
String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if (isDownloadsDocument(imageUri)) {
String id = DocumentsContract.getDocumentId(imageUri);
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} else if (isMediaDocument(imageUri)) {
String docId = DocumentsContract.getDocumentId(imageUri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = new String[] { split[1] };
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} // MediaStore (and general)
else if ("content".equalsIgnoreCase(imageUri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(imageUri))
return imageUri.getLastPathSegment();
return getDataColumn(context, imageUri, null, null);
}
// File
else if ("file".equalsIgnoreCase(imageUri.getScheme())) {
return imageUri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
String column = MediaStore.Images.Media.DATA;
String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
================================================
FILE: settings.gradle
================================================
include ':library', ':demo'