diff options
Diffstat (limited to 'src/com/ruesga/android/wallpapers/photophase/effects')
13 files changed, 1494 insertions, 0 deletions
diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java new file mode 100644 index 0000000..857fdc6 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/BlurEffect.java @@ -0,0 +1,60 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A blur effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class BlurEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " float step = 0.02;\n" + + " vec3 c1 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t - step)).bgr;\n" + + " vec3 c2 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t + step)).bgr;\n" + + " vec3 c3 = texture2D(tex_sampler, vec2(v_texcoord.s - step, v_texcoord.t + step)).bgr;\n" + + " vec3 c4 = texture2D(tex_sampler, vec2(v_texcoord.s + step, v_texcoord.t - step)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = (c1 + c2 + c3 + c4) / 4.0;\n" + + "}"; + + /** + * Constructor of <code>BlurEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public BlurEffect(EffectContext ctx, String name) { + super(ctx, BlurEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java b/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java new file mode 100644 index 0000000..796cc2d --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/Effects.java @@ -0,0 +1,338 @@ +/* + * 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.effects; + +import android.graphics.Color; +import android.media.effect.Effect; +import android.media.effect.EffectContext; +import android.media.effect.EffectFactory; + +import com.ruesga.android.wallpapers.photophase.preferences.PreferencesProvider.Preferences; +import com.ruesga.android.wallpapers.photophase.utils.Utils; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A class that manages all the supported effects + */ +public class Effects { + + /** + * Enumeration of the supported effects + */ + public enum EFFECTS { + /** + * @see PhotoPhaseEffectFactory#EFFECT_NULL + */ + NO_EFFECT, + /** + * @see EffectFactory#EFFECT_AUTOFIX + */ + AUTOFIX, + /** + * @see PhotoPhaseEffectFactory#EFFECT_BLUR + */ + BLUR, + /** + * @see EffectFactory#EFFECT_CROSSPROCESS + */ + CROSSPROCESS, + /** + * @see EffectFactory#EFFECT_DOCUMENTARY + */ + DOCUMENTARY, + /** + * @see EffectFactory#EFFECT_DUOTONE + */ + DUOTONE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_EMBOSS + */ + EMBOSS, + /** + * @see EffectFactory#EFFECT_FISHEYE + */ + FISHEYE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_GLOW + */ + GLOW, + /** + * @see EffectFactory#EFFECT_GRAIN + */ + GRAIN, + /** + * @see EffectFactory#EFFECT_GRAYSCALE + */ + GRAYSCALE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_HALFTONE + */ + HALFTONE, + /** + * @see EffectFactory#EFFECT_LOMOISH + */ + LOMOISH, + /** + * @see PhotoPhaseEffectFactory#EFFECT_MIRROR + */ + MIRROR, + /** + * @see EffectFactory#EFFECT_NEGATIVE + */ + NEGATIVE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_OUTLINE + */ + OUTLINE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_PIXELATE + */ + PIXELATE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_POPART + */ + POPART, + /** + * @see EffectFactory#EFFECT_POSTERIZE + */ + POSTERIZE, + /** + * @see EffectFactory#EFFECT_SATURATE + */ + SATURATE, + /** + * @see PhotoPhaseEffectFactory#EFFECT_SCANLINES + */ + SCANLINES, + /** + * @see EffectFactory#EFFECT_SEPIA + */ + SEPIA, + /** + * @see EffectFactory#EFFECT_TEMPERATURE + */ + TEMPERATURE, + /** + * @see EffectFactory#EFFECT_TINT + */ + TINT, + /** + * @see EffectFactory#EFFECT_VIGNETTE + */ + VIGNETTE; + + /** + * Method that returns the effect from its ordinal position + * + * @param ordinal The ordinal position + * @return EFFECTS The effect or null if wasn't found + */ + public static EFFECTS fromOrdinal(int ordinal) { + for (EFFECTS effect : EFFECTS.values()) { + if (effect.ordinal() == ordinal) { + return effect; + } + } + return null; + } + } + + private final Map<EFFECTS, Effect> mCachedEffects; + private final EffectContext mEffectContext; + + /** + * Constructor of <code>Effects</code> + * + * @param effectContext The current effect context + */ + public Effects(EffectContext effectContext) { + super(); + mCachedEffects = new HashMap<Effects.EFFECTS, Effect>(); + mEffectContext = effectContext; + } + + /** + * Method that that release the cached data + */ + public void release() { + if (mCachedEffects != null) { + for (Effect effect : mCachedEffects.values()) { + effect.release(); + } + mCachedEffects.clear(); + } + } + + /** + * Method that return the next effect to use with the picture. + * + * @return Effect The next effect to use or null if no need to apply any effect + */ + @SuppressWarnings("boxing") + public Effect getNextEffect() { + // Get a new instance of a effect factory + EffectFactory effectFactory = mEffectContext.getFactory(); + Effect effect = null; + + // Get an effect based on the user preference + List<EFFECTS> effects = Arrays.asList(Preferences.General.Effects.getEffectTypes()); + EFFECTS nextEffect = null; + if (effects.size() > 0) { + int low = 0; + int high = effects.size() - 1; + int pos = Utils.getNextRandom(low, high); + nextEffect = effects.get(pos); + } + if (nextEffect == null) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); + mCachedEffects.put(nextEffect, effect); + } + return effect; + } + + // Has a cached effect? + if (mCachedEffects.containsKey(nextEffect)) { + return mCachedEffects.get(nextEffect); + } + + // Select the effect if is available + if (nextEffect.compareTo(EFFECTS.AUTOFIX) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_AUTOFIX)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_AUTOFIX); + effect.setParameter("scale", 0.5f); + } + } else if (nextEffect.compareTo(EFFECTS.BLUR) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_BLUR)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_BLUR); + } + } else if (nextEffect.compareTo(EFFECTS.CROSSPROCESS) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_CROSSPROCESS)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_CROSSPROCESS); + } + } else if (nextEffect.compareTo(EFFECTS.DOCUMENTARY) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DOCUMENTARY)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_DOCUMENTARY); + } + } else if (nextEffect.compareTo(EFFECTS.DUOTONE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_DUOTONE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_DUOTONE); + effect.setParameter("first_color", Color.parseColor("#FF8CACFF")); + effect.setParameter("second_color", Color.WHITE); + } + } else if (nextEffect.compareTo(EFFECTS.EMBOSS) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_EMBOSS)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_EMBOSS); + } + } else if (nextEffect.compareTo(EFFECTS.FISHEYE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_FISHEYE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_FISHEYE); + effect.setParameter("scale", 1.0f); + } + } else if (nextEffect.compareTo(EFFECTS.GLOW) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_GLOW)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_GLOW); + } + } else if (nextEffect.compareTo(EFFECTS.GRAIN) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAIN)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAIN); + effect.setParameter("strength", 1.0f); + } + } else if (nextEffect.compareTo(EFFECTS.GRAYSCALE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_GRAYSCALE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_GRAYSCALE); + } + } else if (nextEffect.compareTo(EFFECTS.HALFTONE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_HALFTONE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_HALFTONE); + effect.setParameter("strength", 8.0f); + } + } else if (nextEffect.compareTo(EFFECTS.MIRROR) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_MIRROR)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_MIRROR); + } + } else if (nextEffect.compareTo(EFFECTS.LOMOISH) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_LOMOISH)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_LOMOISH); + } + } else if (nextEffect.compareTo(EFFECTS.NEGATIVE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_NEGATIVE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_NEGATIVE); + } + } else if (nextEffect.compareTo(EFFECTS.OUTLINE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_OUTLINE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_OUTLINE); + } + } else if (nextEffect.compareTo(EFFECTS.PIXELATE) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_PIXELATE)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_PIXELATE); + effect.setParameter("strength", 8.0f); + } + } else if (nextEffect.compareTo(EFFECTS.POPART) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_POPART)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_POPART); + } + } else if (nextEffect.compareTo(EFFECTS.POSTERIZE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_POSTERIZE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_POSTERIZE); + } + } else if (nextEffect.compareTo(EFFECTS.SATURATE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SATURATE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_SATURATE); + effect.setParameter("scale", .5f); + } + } else if (nextEffect.compareTo(EFFECTS.SCANLINES) == 0) { + if (EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_SCANLINES)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_SCANLINES); + } + } else if (nextEffect.compareTo(EFFECTS.SEPIA) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_SEPIA)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_SEPIA); + } + } else if (nextEffect.compareTo(EFFECTS.TEMPERATURE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TEMPERATURE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_TEMPERATURE); + effect.setParameter("scale", .9f); + } + } else if (nextEffect.compareTo(EFFECTS.TINT) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_TINT)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_TINT); + } + } else if (nextEffect.compareTo(EFFECTS.VIGNETTE) == 0) { + if (EffectFactory.isEffectSupported(EffectFactory.EFFECT_VIGNETTE)) { + effect = effectFactory.createEffect(EffectFactory.EFFECT_VIGNETTE); + effect.setParameter("scale", .5f); + } + } + + // Instead of not to apply any effect, just use one null effect to follow the same + // effect model. This allow to use the same height when Effect.apply is applied for all + // the frames + if (effect == null && EffectFactory.isEffectSupported(PhotoPhaseEffectFactory.EFFECT_NULL)) { + effect = effectFactory.createEffect(PhotoPhaseEffectFactory.EFFECT_NULL); + nextEffect = EFFECTS.NO_EFFECT; + } + + // Cache the effects + mCachedEffects.put(nextEffect, effect); + return effect; + } +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java new file mode 100644 index 0000000..b67458b --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/EmbossEffect.java @@ -0,0 +1,68 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * An emboss effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class EmbossEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 rr = -4.0 * t1 - 4.0 * t2 - 4.0 * t4 + 12.0 * t5;\n" + + " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = vec3(y, y, y) + 0.3;\n" + + "}"; + + /** + * Constructor of <code>EmbossEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public EmbossEffect(EffectContext ctx, String name) { + super(ctx, EmbossEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java new file mode 100644 index 0000000..25f1143 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/GlowEffect.java @@ -0,0 +1,69 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A glow effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class GlowEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + + " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + + " vec3 rr = sqrt(xx * xx + yy * yy);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = rr * 2.0 * t5;\n" + + "}"; + + /** + * Constructor of <code>GlowEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public GlowEffect(EffectContext ctx, String name) { + super(ctx, GlowEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java new file mode 100644 index 0000000..7f7bb4c --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/HalftoneEffect.java @@ -0,0 +1,128 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; +import android.opengl.GLES20; +import android.util.Log; + +import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; + +/** + * A halftone effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * <tr> + * <td><code>strength</code></td> + * <td>The halftone strength.</td> + * <td>Positive float (>0). Higher numbers produce smallest points.</td> + * </tr> + * </table> + */ +public class HalftoneEffect extends PhotoPhaseEffect { + + private static final String TAG = "HalftoneEffect"; + + private static final String STRENGTH_PARAMETER = "strength"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "uniform float strength;\n" + + "void main(void)\n" + + "{\n" + + " float offx = floor(v_texcoord.s / (strength * step_w));\n" + + " float offy = floor(v_texcoord.t / (strength * step_h));\n" + + " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + + " vec2 prc = fract(v_texcoord.st / vec2(strength * step_w, strength * step_h));\n" + + " vec2 pw = pow(abs(prc - 0.5), vec2(2.0));\n" + + " float rs = pow(0.45, 2.0);\n" + + " float gr = smoothstep(rs - 0.1, rs + 0.1, pw.x + pw.y);\n" + + " float y = (res.r + res.g + res.b) / 3.0; \n" + + " vec3 ra = res / y;\n" + + " float ls = 0.3;\n" + + " float lb = ceil(y / ls);\n" + + " float lf = ls * lb + 0.3;\n" + + " res = lf * res;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = mix(res, vec3(0.1, 0.1, 0.1), gr);\n" + + "}"; + + private float mStrength = 16.0f; + private int mStepsHandle; + + /** + * Constructor of <code>HalftoneEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public HalftoneEffect(EffectContext ctx, String name) { + super(ctx, HalftoneEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void init(String vertexShader, String fragmentShader) { + super.init(vertexShader, fragmentShader); + + // Parameters + mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + /** + * {@inheritDoc} + */ + @Override + void applyParameters() { + // Set parameters + GLES20.glUniform1f(mStepsHandle, mStrength); + GLESUtil.glesCheckError("glUniform1f"); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { + try { + float strength = Float.parseFloat(value.toString()); + if (strength <= 0) { + Log.w(TAG, "strength parameter must be >= 0"); + return; + } + mStrength = strength; + } catch (NumberFormatException ex) { + // Ignore + } + } + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java new file mode 100644 index 0000000..d9fef06 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/MirrorEffect.java @@ -0,0 +1,63 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A mirror effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class MirrorEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " vec2 off = vec2(0.0, 0.0);\n" + + " if (v_texcoord.t > 0.5) {\n" + + " off.t = 1.0 - v_texcoord.t;\n" + + " off.s = v_texcoord.s;\n" + + " } else {\n" + + " off = v_texcoord;\n" + + " }\n" + + " vec3 color = texture2D(tex_sampler, vec2(off)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = color;\n" + + "}"; + + /** + * Constructor of <code>MirrorEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public MirrorEffect(EffectContext ctx, String name) { + super(ctx, MirrorEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java new file mode 100644 index 0000000..8bc6c42 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/NullEffect.java @@ -0,0 +1,61 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * This effect only copies the source texture to the destination texture.<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class NullEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" + + "}"; + + /** + * Constructor of <code>NullEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public NullEffect(EffectContext ctx, String name) { + super(ctx, NullEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void apply(int inputTexId) { + // Nothing to draw + } +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java new file mode 100644 index 0000000..2652b89 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/OutlineEffect.java @@ -0,0 +1,74 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * An outline effect (highlight edges)<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class OutlineEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "void main(void)\n" + + "{\n" + + " vec3 t1 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t2 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t3 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y - step_h)).bgr;\n" + + " vec3 t4 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y)).bgr;\n" + + " vec3 t5 = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " vec3 t6 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y)).bgr;\n" + + " vec3 t7 = texture2D(tex_sampler, vec2(v_texcoord.x - step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t8 = texture2D(tex_sampler, vec2(v_texcoord.x, v_texcoord.y + step_h)).bgr;\n" + + " vec3 t9 = texture2D(tex_sampler, vec2(v_texcoord.x + step_w, v_texcoord.y + step_h)).bgr;\n" + + " vec3 xx= t1 + 2.0*t2 + t3 - t7 - 2.0*t8 - t9;\n" + + " vec3 yy = t1 - t3 + 2.0*t4 - 2.0*t6 + t7 - t9;\n" + + " vec3 rr = sqrt(xx * xx + yy * yy);\n" + + " float y = (rr.r + rr.g + rr.b) / 3.0;\n" + + " if (y > 0.2)\n" + + " rr = vec3(0.0, 0.0, 0.0);\n" + + " else\n" + + " rr = vec3(1.0, 1.0, 1.0);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = rr;\n" + + "}"; + + /** + * Constructor of <code>OutlineEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public OutlineEffect(EffectContext ctx, String name) { + super(ctx, OutlineEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java new file mode 100644 index 0000000..3d2c0fd --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffect.java @@ -0,0 +1,268 @@ +/* + * 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.effects; + +import android.media.effect.Effect; +import android.media.effect.EffectContext; +import android.media.effect.EffectFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; + +import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +/** + * An abstract class definition for all the PhotoPhase custom effects + */ +public abstract class PhotoPhaseEffect extends Effect { + + private static final int FLOAT_SIZE_BYTES = 4; + + private static final String MCA_IDENTITY_EFFECT = "IdentityEffect"; + + 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 = vec4(a_position.xy, 0.0, 1.0);\n" + + " gl_Position = sign(gl_Position);\n" + + " v_texcoord = a_texcoord;\n" + + "}\n"; + + private static final float[] TEX_VERTICES = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + private static final float[] POS_VERTICES = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; + + private final int GL_STATE_FBO = 0; + private final int GL_STATE_PROGRAM = 1; + private final int GL_STATE_ARRAYBUFFER = 2; + private final int GL_STATE_COUNT = 3; + + private int[] mOldState = new int[GL_STATE_COUNT]; + + private final EffectContext mEffectContext; + private final String mName; + + private Effect mIdentityEffect; + + int mProgram; + int mTexSamplerHandle; + int mTexCoordHandle; + int mPosCoordHandle; + + FloatBuffer mTexVertices; + FloatBuffer mPosVertices; + + /** + * An abstract constructor of <code>Effect</code> to follow the rules + * defined by {@link EffectFactory}. + * + * @param ctx The effect context + * @param name The effect name + */ + public PhotoPhaseEffect(EffectContext ctx, String name) { + super(); + mEffectContext = ctx; + mName = name; + + // Stand on MCA identity effect for the initialization work + EffectFactory effectFactory = mEffectContext.getFactory(); + mIdentityEffect = effectFactory.createEffect(MCA_IDENTITY_EFFECT); + } + + /** + * Method that initializes the effect + */ + void init(String vertexShader, String fragmentShader) { + // Create program + mProgram = GLESUtil.createProgram(vertexShader, fragmentShader); + + // Bind attributes and uniforms + mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "tex_sampler"); + GLESUtil.glesCheckError("glGetUniformLocation"); + mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord"); + GLESUtil.glesCheckError("glGetAttribLocation"); + mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position"); + GLESUtil.glesCheckError("glGetAttribLocation"); + + // 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); + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return mName; + } + + /** + * Method that returns the effect context + * + * @return EffectContext The effect context + */ + public EffectContext getEffectContext() { + return mEffectContext; + } + + /** + * {@inheritDoc} + */ + @Override + public final synchronized void apply(int inputTexId, int width, int height, int outputTexId) { + // Save the GLES state + saveGLState(); + + try { + // Create a framebuffer object and call the effect apply method to draw the effect + int[] fb = new int[1]; + GLES20.glGenFramebuffers(1, fb, 0); + GLESUtil.glesCheckError("glGenFramebuffers"); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[0]); + GLESUtil.glesCheckError("glBindFramebuffer"); + + // Render on the whole framebuffer + GLES20.glViewport(0, 0, width, height); + GLESUtil.glesCheckError("glViewport"); + + // Create a new output texture (Use the MCA identity to clone the input to the output) + mIdentityEffect.apply(inputTexId, width, height, outputTexId); + + // Create the framebuffer + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20. GL_TEXTURE_2D, outputTexId, 0); + GLESUtil.glesCheckError("glFramebufferTexture2D"); + + // Check if the buffer was built successfully + if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { + // Something when wrong. Throw an exception + GLESUtil.glesCheckError("glCheckFramebufferStatus"); + int error = GLES20.glGetError(); + throw new android.opengl.GLException(error, GLUtils.getEGLErrorString(error)); + } + + // Apply the effect + apply(inputTexId); + + } finally { + // Restore the GLES state + restoreGLState(); + } + + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + // Ignore + } + + /** + * {@inheritDoc} + */ + @Override + public void release() { + if (GLES20.glIsProgram(mProgram)) { + GLES20.glDeleteProgram(mProgram); + GLESUtil.glesCheckError("glDeleteProgram"); + } + mTexVertices = null; + mPosVertices = null; + } + + /** + * Method that applies the effect. + * + * @param inputTexId The input texture + */ + void apply(int inputTexId) { + // Use our shader program + GLES20.glUseProgram(mProgram); + GLESUtil.glesCheckError("glUseProgram"); + + // Disable blending + GLES20.glDisable(GLES20.GL_BLEND); + GLESUtil.glesCheckError("glDisable"); + + // Set the vertex attributes + GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexVertices); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mTexCoordHandle); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mPosVertices); + GLESUtil.glesCheckError("glVertexAttribPointer"); + GLES20.glEnableVertexAttribArray(mPosCoordHandle); + GLESUtil.glesCheckError("glEnableVertexAttribArray"); + + // Set parameters + applyParameters(); + + // Set the input texture + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLESUtil.glesCheckError("glActiveTexture"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, inputTexId); + GLESUtil.glesCheckError("glBindTexture"); + GLES20.glUniform1i(mTexSamplerHandle, 0); + GLESUtil.glesCheckError("glUniform1i"); + + // Draw + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLESUtil.glesCheckError("glClearColor"); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + GLESUtil.glesCheckError("glClear"); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLESUtil.glesCheckError("glDrawArrays"); + + // Disable attributes + GLES20.glDisableVertexAttribArray(mTexCoordHandle); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + GLES20.glDisableVertexAttribArray(mPosCoordHandle); + GLESUtil.glesCheckError("glDisableVertexAttribArray"); + } + + /** + * Method that applies the parameters of the effect. + */ + void applyParameters() { + // Do nothing + } + + + private final void saveGLState() { + GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, mOldState, GL_STATE_FBO); + GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, mOldState, GL_STATE_PROGRAM); + GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, mOldState, GL_STATE_ARRAYBUFFER); + } + + private final void restoreGLState() { + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mOldState[GL_STATE_FBO]); + GLES20.glUseProgram(mOldState[GL_STATE_PROGRAM]); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mOldState[GL_STATE_ARRAYBUFFER]); + } +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java new file mode 100644 index 0000000..0e3a027 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/PhotoPhaseEffectFactory.java @@ -0,0 +1,124 @@ +/* + * 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.effects; + +/** + * A class that defines the own PhotoPhase's effects implementation. This class follows the + * rules of the MCA aosp library. + */ +public class PhotoPhaseEffectFactory { + + /** + * <p>Applies a blur effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_BLUR = "com.ruesga.android.wallpapers.photophase.effects.BlurEffect"; + + /** + * <p>Applies an emboss effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_EMBOSS = "com.ruesga.android.wallpapers.photophase.effects.EmbossEffect"; + + /** + * <p>Applies a glow effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_GLOW = "com.ruesga.android.wallpapers.photophase.effects.GlowEffect"; + + /** + * <p>Applies a halftone effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * <tr> + * <td><code>strength</code></td> + * <td>The halftone steps multiplier.</td> + * <td>Positive float (>0). Higher numbers produce smallest points</td> + * </tr> + * </table> + */ + public static final String EFFECT_HALFTONE = "com.ruesga.android.wallpapers.photophase.effects.HalftoneEffect"; + + /** + * <p>Applies a mirror effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_MIRROR = "com.ruesga.android.wallpapers.photophase.effects.MirrorEffect"; + + /** + * <p>Doesn't apply any effect.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_NULL = "com.ruesga.android.wallpapers.photophase.effects.NullEffect"; + + /** + * <p>Applies an outline effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_OUTLINE = "com.ruesga.android.wallpapers.photophase.effects.OutlineEffect"; + + /** + * <p>Applies a pixelate effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * <tr> + * <td><code>strength</code></td> + * <td>The pixelate steps multiplier.</td> + * <td>Positive float (>0). Higher numbers produce more pixelation.</td> + * </tr> + * </table> + */ + public static final String EFFECT_PIXELATE = "com.ruesga.android.wallpapers.photophase.effects.PixelateEffect"; + + /** + * <p>Applies a pop art (Warhol) effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_POPART = "com.ruesga.android.wallpapers.photophase.effects.PopArtEffect"; + + /** + * <p>Applies a TV scan line effect to the image.</p> + * <p>Available parameters:</p> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ + public static final String EFFECT_SCANLINES = "com.ruesga.android.wallpapers.photophase.effects.ScanlinesEffect"; +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java new file mode 100644 index 0000000..1f7ea70 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/PixelateEffect.java @@ -0,0 +1,118 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; +import android.opengl.GLES20; +import android.util.Log; + +import com.ruesga.android.wallpapers.photophase.utils.GLESUtil; + +/** + * A pixelate effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * <tr> + * <td><code>strength</code></td> + * <td>The pixelate strength.</td> + * <td>Positive float (>0). Higher numbers produce more pixelation.</td> + * </tr> + * </table> + */ +public class PixelateEffect extends PhotoPhaseEffect { + + private static final String TAG = "PixelateEffect"; + + private static final String STRENGTH_PARAMETER = "strength"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "const float step_w = 0.0015625;\n" + + "const float step_h = 0.0027778;\n" + + "uniform float strength;\n" + + "void main(void)\n" + + "{\n" + + " float offx = floor(v_texcoord.s / (strength * step_w));\n" + + " float offy = floor(v_texcoord.t / (strength * step_h));\n" + + " vec3 res = texture2D(tex_sampler, vec2(offx * strength * step_w , offy * strength * step_h)).bgr;\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = res;\n" + + "}"; + + private float mStrength = 8.0f; + private int mStepsHandle; + + /** + * Constructor of <code>PixelateEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public PixelateEffect(EffectContext ctx, String name) { + super(ctx, PixelateEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + + /** + * {@inheritDoc} + */ + @Override + void init(String vertexShader, String fragmentShader) { + super.init(vertexShader, fragmentShader); + + // Parameters + mStepsHandle = GLES20.glGetUniformLocation(mProgram, "strength"); + GLESUtil.glesCheckError("glGetUniformLocation"); + } + + /** + * {@inheritDoc} + */ + @Override + void applyParameters() { + // Set parameters + GLES20.glUniform1f(mStepsHandle, mStrength); + GLESUtil.glesCheckError("glUniform1f"); + } + + /** + * {@inheritDoc} + */ + @Override + public void setParameter(String parameterKey, Object value) { + if (parameterKey.compareTo(STRENGTH_PARAMETER) == 0) { + try { + float strength = Float.parseFloat(value.toString()); + if (strength <= 0) { + Log.w(TAG, "strength parameter must be >= 0"); + return; + } + mStrength = strength; + } catch (NumberFormatException ex) { + // Ignore + } + } + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java new file mode 100644 index 0000000..cda5fb0 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/PopArtEffect.java @@ -0,0 +1,64 @@ +/* + * 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. + */ +// +// Based on the shaders of kodemongki: +// http://kodemongki.blogspot.com.es/2011/06/kameraku-custom-shader-effects-example.html +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A pop art (Warhol) effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class PopArtEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " vec3 col = texture2D(tex_sampler, v_texcoord).bgr;\n" + + " float y = 0.3 *col.r + 0.59 * col.g + 0.11 * col.b;\n" + + " y = y < 0.3 ? 0.0 : (y < 0.6 ? 0.5 : 1.0);\n" + + " if (y == 0.5)\n" + + " col = vec3(0.8, 0.0, 0.0);\n" + + " else if (y == 1.0)\n" + + " col = vec3(0.9, 0.9, 0.0);\n" + + " else\n" + + " col = vec3(0.0, 0.0, 0.0);\n" + + " gl_FragColor.a = 1.0;\n" + + " gl_FragColor.rgb = col;\n" + + "}"; + + /** + * Constructor of <code>PopArtEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public PopArtEffect(EffectContext ctx, String name) { + super(ctx, PopArtEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java b/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java new file mode 100644 index 0000000..8343ed2 --- /dev/null +++ b/src/com/ruesga/android/wallpapers/photophase/effects/ScanlinesEffect.java @@ -0,0 +1,59 @@ +/* + * 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. + */ +// +// Based on the shaders of Max Maischein of App-VideoMixer: +// http://cpansearch.perl.org/src/CORION/App-VideoMixer-0.02/filters/scanlines.glsl +// + +package com.ruesga.android.wallpapers.photophase.effects; + +import android.media.effect.EffectContext; + +/** + * A TV scanline effect<br/> + * <table> + * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> + * </table> + */ +public class ScanlinesEffect extends PhotoPhaseEffect { + + private static final String FRAGMENT_SHADER = + "precision mediump float;\n" + + "uniform sampler2D tex_sampler;\n" + + "uniform float offset;\n" + + "float frequency = 83.0;\n" + + "varying vec2 v_texcoord;\n" + + "void main(void)\n" + + "{\n" + + " float global_pos = (v_texcoord.y + offset) * frequency;\n" + + " float wave_pos = cos((fract(global_pos) - 0.5)*3.14);\n" + + " vec4 pel = texture2D(tex_sampler, v_texcoord);\n" + + " gl_FragColor = mix(vec4(0,0,0,0), pel, wave_pos);\n" + + "}"; + + /** + * Constructor of <code>ScanlinesEffect</code>. + * + * @param ctx The effect context + * @param name The effect name + */ + public ScanlinesEffect(EffectContext ctx, String name) { + super(ctx, ScanlinesEffect.class.getName()); + init(VERTEX_SHADER, FRAGMENT_SHADER); + } + +} |