Monday 21 October 2013

Apply Effects on Image Using Effects Factory Class in Android

The EffectFactory class defines the list of available Effects, and provides functionality to inspect and instantiate them. Some effects may not be available on all platforms, so before creating a certain effect, the application should confirm that the effect is supported on this platform by calling isEffectSupported(String).

EffectFactory class is available from the API level 14. So if you want to use this class then your application must have minSdk as 14. 


This class contains the predefined constant values which are used to apply different effects on Image.
Below are the filters constant which you can check out.

Constants
StringEFFECT_AUTOFIXAttempts to auto-fix the image based on histogram equalization.
StringEFFECT_BACKDROPPERReplaces the background of the input frames with frames from a selected video.
StringEFFECT_BITMAPOVERLAYOverlays a bitmap (with premultiplied alpha channel) onto the input image.
StringEFFECT_BLACKWHITEAdjusts the range of minimal and maximal color pixel intensities.
StringEFFECT_BRIGHTNESSAdjusts the brightness of the image.
StringEFFECT_CONTRASTAdjusts the contrast of the image.
StringEFFECT_CROPCrops an upright rectangular area from the image.
StringEFFECT_CROSSPROCESSApplies a cross process effect on image, in which the red and green
 channels are enhanced while the blue channel is restricted.
StringEFFECT_DOCUMENTARYApplies black and white documentary style effect on image..
StringEFFECT_DUOTONERepresentation of photo using only two color tones.
StringEFFECT_FILLLIGHTApplies back-light filling to the image.
StringEFFECT_FISHEYEApplies a fisheye lens distortion to the image.
StringEFFECT_FLIPFlips image vertically and/or horizontally.
StringEFFECT_GRAINApplies film grain effect to image.
StringEFFECT_GRAYSCALEConverts image to grayscale.
StringEFFECT_LOMOISHApplies lomo-camera style effect to image.
StringEFFECT_NEGATIVEInverts the image colors.
StringEFFECT_POSTERIZEApplies posterization effect to image.
StringEFFECT_REDEYERemoves red eyes on specified region.
StringEFFECT_ROTATERotates the image.
StringEFFECT_SATURATEAdjusts color saturation of image.
StringEFFECT_SEPIAConverts image to sepia tone.
StringEFFECT_SHARPENSharpens the image.
StringEFFECT_STRAIGHTENRotates the image according to the specified angle, and crops the
 image so that no non-image portions are visible.
StringEFFECT_TEMPERATUREAdjusts color temperature of the image.
StringEFFECT_TINTTints the photo with specified color.
StringEFFECT_VIGNETTEAdds a vignette effect to image, i.e.
Switch for More details.

Here is code of which i have implemented.

EffectFilterActivity.java

 import android.app.Activity;  
 import android.graphics.Bitmap;  
 import android.graphics.BitmapFactory;  
 import android.graphics.Color;  
 import android.os.Bundle;  
 import android.view.Menu;  
 import android.view.MenuInflater;  
 import android.view.MenuItem;  
 import android.view.Window;  
 import android.media.effect.Effect;  
 import android.media.effect.EffectContext;  
 import android.media.effect.EffectFactory;  
 import android.opengl.GLES20;  
 import android.opengl.GLSurfaceView;  
 import android.opengl.GLUtils;  
 import javax.microedition.khronos.egl.EGLConfig;  
 import javax.microedition.khronos.opengles.GL10;  
 public class EffectsFilterActivity extends Activity implements GLSurfaceView.Renderer {  
   private GLSurfaceView mEffectView;  
   private int[] mTextures = new int[2];  
   private EffectContext mEffectContext;  
   private Effect mEffect;  
   private TextureRenderer mTexRenderer = new TextureRenderer();  
   private int mImageWidth;  
   private int mImageHeight;  
   private boolean mInitialized = false;  
   int mCurrentEffect;  
   private volatile boolean saveFrame;  
   public void setCurrentEffect(int effect) {  
     mCurrentEffect = effect;  
   }  
   @Override  
   public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     requestWindowFeature(Window.FEATURE_NO_TITLE);  
     setContentView(R.layout.main);  
     /**  
      * Initialise the renderer and tell it to only render when  
      * Explicit requested with the RENDERMODE_WHEN_DIRTY option  
      */  
     mEffectView = (GLSurfaceView) findViewById(R.id.effectsview);  
     mEffectView.setEGLContextClientVersion(2);  
     mEffectView.setRenderer(this);  
     mEffectView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);  
     mCurrentEffect = R.id.none;  
   }  
   private void loadTextures() {  
     // Generate textures  
     GLES20.glGenTextures(2, mTextures, 0);  
     // Load input bitmap  
     Bitmap bitmap = BitmapFactory.decodeResource(getResources(),  
         R.drawable.puppy);  
     mImageWidth = bitmap.getWidth();  
     mImageHeight = bitmap.getHeight();  
     mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);  
     // Upload to texture  
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);  
     GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);  
     // Set texture parameters  
     GLToolbox.initTexParams();  
   }  
   private void initEffect() {  
     EffectFactory effectFactory = mEffectContext.getFactory();  
     if (mEffect != null) {  
       mEffect.release();  
     }  
     /**  
      * Initialize the correct effect based on the selected menu/action item  
      */  
     switch (mCurrentEffect) {  
       case R.id.none:  
         break;  
       case R.id.autofix:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_AUTOFIX);  
         mEffect.setParameter("scale", 0.5f);  
         break;  
       case R.id.bw:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_BLACKWHITE);  
         mEffect.setParameter("black", .1f);  
         mEffect.setParameter("white", .7f);  
         break;  
       case R.id.brightness:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_BRIGHTNESS);  
         mEffect.setParameter("brightness", 2.0f);  
         break;  
       case R.id.contrast:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_CONTRAST);  
         mEffect.setParameter("contrast", 1.4f);  
         break;  
       case R.id.crossprocess:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_CROSSPROCESS);  
         break;  
       case R.id.documentary:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_DOCUMENTARY);  
         break;  
       case R.id.duotone:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_DUOTONE);  
         mEffect.setParameter("first_color", Color.YELLOW);  
         mEffect.setParameter("second_color", Color.DKGRAY);  
         break;  
       case R.id.filllight:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_FILLLIGHT);  
         mEffect.setParameter("strength", .8f);  
         break;  
       case R.id.fisheye:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_FISHEYE);  
         mEffect.setParameter("scale", .5f);  
         break;  
       case R.id.flipvert:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_FLIP);  
         mEffect.setParameter("vertical", true);  
         break;  
       case R.id.fliphor:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_FLIP);  
         mEffect.setParameter("horizontal", true);  
         break;  
       case R.id.grain:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_GRAIN);  
         mEffect.setParameter("strength", 1.0f);  
         break;  
       case R.id.grayscale:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_GRAYSCALE);  
         break;  
       case R.id.lomoish:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_LOMOISH);  
         break;  
       case R.id.negative:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_NEGATIVE);  
         break;  
       case R.id.posterize:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_POSTERIZE);  
         break;  
       case R.id.rotate:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_ROTATE);  
         mEffect.setParameter("angle", 180);  
         break;  
       case R.id.saturate:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_SATURATE);  
         mEffect.setParameter("scale", .5f);  
         break;  
       case R.id.sepia:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_SEPIA);  
         break;  
       case R.id.sharpen:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_SHARPEN);  
         break;  
       case R.id.temperature:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_TEMPERATURE);  
         mEffect.setParameter("scale", .9f);  
         break;  
       case R.id.tint:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_TINT);  
         mEffect.setParameter("tint", Color.MAGENTA);  
         break;  
       case R.id.vignette:  
         mEffect = effectFactory.createEffect(  
             EffectFactory.EFFECT_VIGNETTE);  
         mEffect.setParameter("scale", .5f);  
         break;  
       default:  
         break;  
     }  
   }  
   private void applyEffect() {  
     mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);  
   }  
   private void renderResult() {  
     if (mCurrentEffect != R.id.none) {  
       // if no effect is chosen, just render the original bitmap  
       mTexRenderer.renderTexture(mTextures[1]);  
     }  
     else {  
        saveFrame=true;  
       // render the result of applyEffect()  
       mTexRenderer.renderTexture(mTextures[0]);  
     }  
   }  
   @Override  
   public void onDrawFrame(GL10 gl) {  
     if (!mInitialized) {  
       //Only need to do this once  
       mEffectContext = EffectContext.createWithCurrentGlContext();  
       mTexRenderer.init();  
       loadTextures();  
       mInitialized = true;  
     }  
     if (mCurrentEffect != R.id.none) {  
       //if an effect is chosen initialize it and apply it to the texture  
       initEffect();  
       applyEffect();  
     }  
     renderResult();  
     if (saveFrame) {  
  saveBitmap(takeScreenshot(gl));  
  }  
   }  
   @Override  
   public void onSurfaceChanged(GL10 gl, int width, int height) {  
     if (mTexRenderer != null) {  
       mTexRenderer.updateViewSize(width, height);  
     }  
   }  
  private void saveBitmap(Bitmap bitmap) {  
  String root = Environment.getExternalStorageDirectory().toString();  
    File myDir = new File(root + "/saved_images");    
    myDir.mkdirs();  
    Random generator = new Random();  
    int n = 10000;  
    n = generator.nextInt(n);  
    String fname = "Image-"+ n +".jpg";  
    File file = new File (myDir, fname);  
    if (file.exists ()) file.delete ();   
    try {  
       FileOutputStream out = new FileOutputStream(file);  
       bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);  
       out.flush();  
       out.close();  
       Log.i("TAG", "Image SAVED=========="+file.getAbsolutePath());  
    } catch (Exception e) {  
       e.printStackTrace();  
    }  
  }  
  public Bitmap takeScreenshot(GL10 mGL) {  
  final int mWidth = mEffectView.getWidth();  
  final int mHeight = mEffectView.getHeight();  
  IntBuffer ib = IntBuffer.allocate(mWidth * mHeight);  
  IntBuffer ibt = IntBuffer.allocate(mWidth * mHeight);  
  mGL.glReadPixels(0, 0, mWidth, mHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);  
   // Convert upside down mirror-reversed image to right-side up normal  
  // image.  
  for (int i = 0; i < mHeight; i++) {  
   for (int j = 0; j < mWidth; j++) {  
   ibt.put((mHeight - i - 1) * mWidth + j, ib.get(i * mWidth + j));  
   }  
  }  
   Bitmap mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);  
  mBitmap.copyPixelsFromBuffer(ibt);  
  return mBitmap;  
  }  
   @Override  
   public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
   }  
   @Override  
   public boolean onCreateOptionsMenu(Menu menu) {  
     MenuInflater inflater = getMenuInflater();  
     inflater.inflate(R.menu.main, menu);  
     return true;  
   }  
   @Override  
   public boolean onOptionsItemSelected(MenuItem item) {  
     setCurrentEffect(item.getItemId());  
     mEffectView.requestRender();  
     return true;  
   }  
 }  

main.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="fill_parent"  
   android:layout_height="fill_parent"  
   android:orientation="vertical" >  
   <android.opengl.GLSurfaceView  
     android:id="@+id/effectsview"  
     android:layout_width="fill_parent"  
     android:layout_height="0dp"  
     android:layout_weight="0.93" />  
 </LinearLayout>  

GLToolbox.java
 import android.opengl.GLES20;  
 public class GLToolbox {  
   public static int loadShader(int shaderType, String source) {  
     int shader = GLES20.glCreateShader(shaderType);  
     if (shader != 0) {  
       GLES20.glShaderSource(shader, source);  
       GLES20.glCompileShader(shader);  
       int[] compiled = new int[1];  
       GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);  
       if (compiled[0] == 0) {  
         String info = GLES20.glGetShaderInfoLog(shader);  
         GLES20.glDeleteShader(shader);  
         shader = 0;  
         throw new RuntimeException("Could not compile shader " +  
         shaderType + ":" + info);  
       }  
     }  
     return shader;  
   }  
   public static int createProgram(String vertexSource,  
       String fragmentSource) {  
     int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);  
     if (vertexShader == 0) {  
       return 0;  
     }  
     int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);  
     if (pixelShader == 0) {  
       return 0;  
     }  
     int program = GLES20.glCreateProgram();  
     if (program != 0) {  
       GLES20.glAttachShader(program, vertexShader);  
       checkGlError("glAttachShader");  
       GLES20.glAttachShader(program, pixelShader);  
       checkGlError("glAttachShader");  
       GLES20.glLinkProgram(program);  
       int[] linkStatus = new int[1];  
       GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus,  
           0);  
       if (linkStatus[0] != GLES20.GL_TRUE) {  
         String info = GLES20.glGetProgramInfoLog(program);  
         GLES20.glDeleteProgram(program);  
         program = 0;  
         throw new RuntimeException("Could not link program: " + info);  
       }  
     }  
     return program;  
   }  
   public static void checkGlError(String op) {  
     int error;  
     while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {  
       throw new RuntimeException(op + ": glError " + error);  
     }  
   }  
   public static void initTexParams() {  
     GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,  
         GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);  
     GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,  
         GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);  
     GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,  
         GLES20.GL_CLAMP_TO_EDGE);  
     GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,  
         GLES20.GL_CLAMP_TO_EDGE);  
   }  
 }  

TextureRender.java
 import android.opengl.GLES20;  
 import java.nio.ByteBuffer;  
 import java.nio.ByteOrder;  
 import java.nio.FloatBuffer;  
 public class TextureRenderer {  
   private int mProgram;  
   private int mTexSamplerHandle;  
   private int mTexCoordHandle;  
   private int mPosCoordHandle;  
   private FloatBuffer mTexVertices;  
   private FloatBuffer mPosVertices;  
   private int mViewWidth;  
   private int mViewHeight;  
   private int mTexWidth;  
   private int mTexHeight;  
   private static final String VERTEX_SHADER =  
     "attribute vec4 a_position;\n" +  
     "attribute vec2 a_texcoord;\n" +  
     "varying vec2 v_texcoord;\n" +  
     "void main() {\n" +  
     " gl_Position = a_position;\n" +  
     " v_texcoord = a_texcoord;\n" +  
     "}\n";  
   private static final String FRAGMENT_SHADER =  
     "precision mediump float;\n" +  
     "uniform sampler2D tex_sampler;\n" +  
     "varying vec2 v_texcoord;\n" +  
     "void main() {\n" +  
     " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +  
     "}\n";  
   private static final float[] TEX_VERTICES = {  
     0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f  
   };  
   private static final float[] POS_VERTICES = {  
     -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f  
   };  
   private static final int FLOAT_SIZE_BYTES = 4;  
   public void init() {  
     // Create program  
     mProgram = GLToolbox.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);  
     // Bind attributes and uniforms  
     mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram,  
         "tex_sampler");  
     mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord");  
     mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position");  
     // Setup coordinate buffers  
     mTexVertices = ByteBuffer.allocateDirect(  
         TEX_VERTICES.length * FLOAT_SIZE_BYTES)  
         .order(ByteOrder.nativeOrder()).asFloatBuffer();  
     mTexVertices.put(TEX_VERTICES).position(0);  
     mPosVertices = ByteBuffer.allocateDirect(  
         POS_VERTICES.length * FLOAT_SIZE_BYTES)  
         .order(ByteOrder.nativeOrder()).asFloatBuffer();  
     mPosVertices.put(POS_VERTICES).position(0);  
   }  
   public void tearDown() {  
     GLES20.glDeleteProgram(mProgram);  
   }  
   public void updateTextureSize(int texWidth, int texHeight) {  
     mTexWidth = texWidth;  
     mTexHeight = texHeight;  
     computeOutputVertices();  
   }  
   public void updateViewSize(int viewWidth, int viewHeight) {  
     mViewWidth = viewWidth;  
     mViewHeight = viewHeight;  
     computeOutputVertices();  
   }  
   public void renderTexture(int texId) {  
     // Bind default FBO  
     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);  
     // Use our shader program  
     GLES20.glUseProgram(mProgram);  
     GLToolbox.checkGlError("glUseProgram");  
     // Set viewport  
     GLES20.glViewport(0, 0, mViewWidth, mViewHeight);  
     GLToolbox.checkGlError("glViewport");  
     // Disable blending  
     GLES20.glDisable(GLES20.GL_BLEND);  
     // Set the vertex attributes  
     GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false,  
         0, mTexVertices);  
     GLES20.glEnableVertexAttribArray(mTexCoordHandle);  
     GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false,  
         0, mPosVertices);  
     GLES20.glEnableVertexAttribArray(mPosCoordHandle);  
     GLToolbox.checkGlError("vertex attribute setup");  
     // Set the input texture  
     GLES20.glActiveTexture(GLES20.GL_TEXTURE0);  
     GLToolbox.checkGlError("glActiveTexture");  
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);  
     GLToolbox.checkGlError("glBindTexture");  
     GLES20.glUniform1i(mTexSamplerHandle, 0);  
     // Draw  
     GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  
     GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);  
     GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);  
   }  
   private void computeOutputVertices() {  
     if (mPosVertices != null) {  
       float imgAspectRatio = mTexWidth / (float)mTexHeight;  
       float viewAspectRatio = mViewWidth / (float)mViewHeight;  
       float relativeAspectRatio = viewAspectRatio / imgAspectRatio;  
       float x0, y0, x1, y1;  
       if (relativeAspectRatio > 1.0f) {  
         x0 = -1.0f / relativeAspectRatio;  
         y0 = -1.0f;  
         x1 = 1.0f / relativeAspectRatio;  
         y1 = 1.0f;  
       } else {  
         x0 = -1.0f;  
         y0 = -relativeAspectRatio;  
         x1 = 1.0f;  
         y1 = relativeAspectRatio;  
       }  
       float[] coords = new float[] { x0, y0, x1, y0, x0, y1, x1, y1 };  
       mPosVertices.put(coords).position(0);  
     }  
   }  
 }  

AndroidManifest.xml
 <?xml version="1.0" encoding="utf-8"?>  
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
   package="com.test.effectsfilter_demo"  
   android:versionCode="1"  
   android:versionName="1.0" >  
   <uses-sdk  
     android:minSdkVersion="14"  
     android:targetSdkVersion="18" />  
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
   <application  
     android:allowBackup="true"  
     android:icon="@drawable/ic_launcher"  
     android:label="@string/app_name"  
     android:theme="@style/AppTheme" >  
     <activity  
       android:name=".EffectsFilterActivity"  
       android:label="@string/app_name" >  
       <intent-filter>  
         <action android:name="android.intent.action.MAIN" />  
         <category android:name="android.intent.category.LAUNCHER" />  
       </intent-filter>  
     </activity>  
   </application>  
 </manifest>  

Output:















Download Code
Enjoy.

Thanks. 

2 comments: