adding media effects sample to sdk

Change-Id: I8fc55e1ac5409c0262bfda930e6db3d34ce6ae08
diff --git a/samples/HelloEffects/Android.mk b/samples/HelloEffects/Android.mk
new file mode 100644
index 0000000..1d9a50a
--- /dev/null
+++ b/samples/HelloEffects/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := HelloEffects
+
+include $(BUILD_PACKAGE)
diff --git a/samples/HelloEffects/AndroidManifest.xml b/samples/HelloEffects/AndroidManifest.xml
new file mode 100644
index 0000000..d3c5f64
--- /dev/null
+++ b/samples/HelloEffects/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        android:versionCode="1"
+        android:versionName="1.0" package="com.example.android.mediafx">
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="15"/>
+    <application android:label="@string/app_name"
+            android:icon="@drawable/ic_launcher"
+            android:debuggable="true"
+            android:hardwareAccelerated="true">
+        <activity android:name="com.example.android.mediafx.HelloEffects"
+                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>
diff --git a/samples/HelloEffects/_index.html b/samples/HelloEffects/_index.html
new file mode 100644
index 0000000..adf1fa8
--- /dev/null
+++ b/samples/HelloEffects/_index.html
@@ -0,0 +1,23 @@
+<!--
+  Copyright 2011 The Android Open Source 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.
+-->
+
+<p>This sample shows how to use the <a href="http://developer.android.com/reference/android/media/effect/package-summary.html">Media
+Effects APIs</a> that were introduced in Android 4.0. These APIs let you apply
+effects to image frames represented as OpenGL ES 2.0 textures. Image frames can
+be images loaded from disk, frames from the device's camera, or other video
+streams.</p>
+
+
diff --git a/samples/HelloEffects/res/drawable-hdpi/ic_launcher.png b/samples/HelloEffects/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..76278e3
--- /dev/null
+++ b/samples/HelloEffects/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/HelloEffects/res/drawable-ldpi/ic_launcher.png b/samples/HelloEffects/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..44ede02
--- /dev/null
+++ b/samples/HelloEffects/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/HelloEffects/res/drawable-mdpi/ic_launcher.png b/samples/HelloEffects/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..162d976
--- /dev/null
+++ b/samples/HelloEffects/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/HelloEffects/res/drawable-xhdpi/ic_launcher.png b/samples/HelloEffects/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..131d3a0
--- /dev/null
+++ b/samples/HelloEffects/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/HelloEffects/res/drawable/puppy.jpg b/samples/HelloEffects/res/drawable/puppy.jpg
new file mode 100644
index 0000000..ef79be2
--- /dev/null
+++ b/samples/HelloEffects/res/drawable/puppy.jpg
Binary files differ
diff --git a/samples/HelloEffects/res/layout/main.xml b/samples/HelloEffects/res/layout/main.xml
new file mode 100644
index 0000000..94c23e2
--- /dev/null
+++ b/samples/HelloEffects/res/layout/main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source 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.
+-->
+<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>
diff --git a/samples/HelloEffects/res/menu/options_menu.xml b/samples/HelloEffects/res/menu/options_menu.xml
new file mode 100644
index 0000000..531a8dc
--- /dev/null
+++ b/samples/HelloEffects/res/menu/options_menu.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/none"
+            android:title="@string/none"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/autofix"
+            android:title="@string/autofix"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/bw"
+            android:title="@string/bw"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/brightness"
+            android:title="@string/brightness"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/contrast"
+            android:title="@string/contrast"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/crossprocess"
+            android:title="@string/crossprocess"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/documentary"
+            android:title="@string/documentary"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/duotone"
+            android:title="@string/duotone"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/filllight"
+            android:title="@string/filllight"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/fisheye"
+            android:title="@string/fisheye"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/flipvert"
+            android:title="@string/flipvert"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/fliphor"
+            android:title="@string/fliphor"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/grain"
+            android:title="@string/grain"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/grayscale"
+            android:title="@string/grayscale"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/lomoish"
+            android:title="@string/lomoish"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/negative"
+            android:title="@string/negative"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/posterize"
+            android:title="@string/posterize"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/rotate"
+            android:title="@string/rotate"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/saturate"
+            android:title="@string/saturate"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/sepia"
+            android:title="@string/sepia"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/sharpen"
+            android:title="@string/sharpen"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/temperature"
+            android:title="@string/temperature"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/tint"
+            android:title="@string/tint"
+            android:showAsAction="never" />
+
+    <item android:id="@+id/vignette"
+            android:title="@string/vignette"
+            android:showAsAction="never" />
+</menu>
diff --git a/samples/HelloEffects/res/values/strings.xml b/samples/HelloEffects/res/values/strings.xml
new file mode 100644
index 0000000..66203d7
--- /dev/null
+++ b/samples/HelloEffects/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source 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.
+-->
+<resources>
+    <string name="app_name">HelloEffects</string>
+    <string name="none">None</string>
+    <string name="autofix">Autofix</string>
+    <string name="bw">Min/Max Color Intensity</string>
+    <string name="brightness">Brightness</string>
+    <string name="contrast">Contrast</string>
+    <string name="crossprocess">Cross Process</string>
+    <string name="documentary">Documentary</string>
+    <string name="duotone">Duo Tone</string>
+    <string name="filllight">Fill Light</string>
+    <string name="fisheye">Fish Eye</string>
+    <string name="flipvert">Flip Vertical</string>
+    <string name="fliphor">Flip Horizontal</string>
+    <string name="grain">Grain</string>
+    <string name="grayscale">Grayscale</string>
+    <string name="lomoish">Lomoish</string>
+    <string name="negative">Negative</string>
+    <string name="posterize">Posterize</string>
+    <string name="rotate">Rotate</string>
+    <string name="saturate">Saturate</string>
+    <string name="sepia">Sepia</string>
+    <string name="sharpen">Sharpen</string>
+    <string name="temperature">Temperature</string>
+    <string name="tint">Tint</string>
+    <string name="vignette">Vignette</string>
+</resources>
diff --git a/samples/HelloEffects/src/com/example/android/mediafx/GLToolbox.java b/samples/HelloEffects/src/com/example/android/mediafx/GLToolbox.java
new file mode 100644
index 0000000..0062fa1
--- /dev/null
+++ b/samples/HelloEffects/src/com/example/android/mediafx/GLToolbox.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 The Android Open Source 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 com.example.android.mediafx;
+
+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);
+   }
+
+}
diff --git a/samples/HelloEffects/src/com/example/android/mediafx/HelloEffects.java b/samples/HelloEffects/src/com/example/android/mediafx/HelloEffects.java
new file mode 100644
index 0000000..cfecce0
--- /dev/null
+++ b/samples/HelloEffects/src/com/example/android/mediafx/HelloEffects.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2012 The Android Open Source 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 com.example.android.mediafx;
+
+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.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 HelloEffects 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;
+
+    public void setCurrentEffect(int effect) {
+        mCurrentEffect = effect;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        /**
+         * Initialize the renderer and tell it to only render when
+         * explicity 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 {
+            // 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();
+    }
+
+    @Override
+    public void onSurfaceChanged(GL10 gl, int width, int height) {
+        if (mTexRenderer != null) {
+            mTexRenderer.updateViewSize(width, height);
+        }
+    }
+
+    @Override
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.options_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        setCurrentEffect(item.getItemId());
+        mEffectView.requestRender();
+        return true;
+    }
+}
diff --git a/samples/HelloEffects/src/com/example/android/mediafx/TextureRenderer.java b/samples/HelloEffects/src/com/example/android/mediafx/TextureRenderer.java
new file mode 100644
index 0000000..9ef47bc
--- /dev/null
+++ b/samples/HelloEffects/src/com/example/android/mediafx/TextureRenderer.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012 The Android Open Source 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 com.example.android.mediafx;
+
+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);
+        }
+    }
+}