/* * Copyright (C) 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. */ package android.media.effect; import java.lang.reflect.Constructor; /** * <p>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 {@link #isEffectSupported(String)}.</p> */ public class EffectFactory { private EffectContext mEffectContext; private final static String[] EFFECT_PACKAGES = { "android.media.effect.effects.", // Default effect package "" // Allows specifying full class path }; /** List of Effects */ /** * <p>Copies the input texture to the output.</p> * <p>Available parameters: None</p> * @hide */ public final static String EFFECT_IDENTITY = "IdentityEffect"; /** * <p>Adjusts the brightness of the image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>brightness</code></td> * <td>The brightness multiplier.</td> * <td>Positive float. 1.0 means no change; larger values will increase brightness.</td> * </tr> * </table> */ public final static String EFFECT_BRIGHTNESS = "android.media.effect.effects.BrightnessEffect"; /** * <p>Adjusts the contrast of the image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>contrast</code></td> * <td>The contrast multiplier.</td> * <td>Float. 1.0 means no change; larger values will increase contrast.</td> * </tr> * </table> */ public final static String EFFECT_CONTRAST = "android.media.effect.effects.ContrastEffect"; /** * <p>Applies a fisheye lens distortion 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>scale</code></td> * <td>The scale of the distortion.</td> * <td>Float, between 0 and 1. Zero means no distortion.</td> * </tr> * </table> */ public final static String EFFECT_FISHEYE = "android.media.effect.effects.FisheyeEffect"; /** * <p>Replaces the background of the input frames with frames from a * selected video. Requires an initial learning period with only the * background visible before the effect becomes active. The effect will wait * until it does not see any motion in the scene before learning the * background and starting the effect.</p> * * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>source</code></td> * <td>A URI for the background video to use. This parameter must be * supplied before calling apply() for the first time.</td> * <td>String, such as from * {@link android.net.Uri#toString Uri.toString()}</td> * </tr> * </table> * * <p>If the update listener is set for this effect using * {@link Effect#setUpdateListener}, it will be called when the effect has * finished learning the background, with a null value for the info * parameter.</p> */ public final static String EFFECT_BACKDROPPER = "android.media.effect.effects.BackDropperEffect"; /** * <p>Attempts to auto-fix the image based on histogram equalization.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>scale</code></td> * <td>The scale of the adjustment.</td> * <td>Float, between 0 and 1. Zero means no adjustment, while 1 indicates the maximum * amount of adjustment.</td> * </tr> * </table> */ public final static String EFFECT_AUTOFIX = "android.media.effect.effects.AutoFixEffect"; /** * <p>Adjusts the range of minimal and maximal color pixel intensities.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>black</code></td> * <td>The value of the minimal pixel.</td> * <td>Float, between 0 and 1.</td> * </tr> * <tr><td><code>white</code></td> * <td>The value of the maximal pixel.</td> * <td>Float, between 0 and 1.</td> * </tr> * </table> */ public final static String EFFECT_BLACKWHITE = "android.media.effect.effects.BlackWhiteEffect"; /** * <p>Crops an upright rectangular area from the image. If the crop region falls outside of * the image bounds, the results are undefined.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>xorigin</code></td> * <td>The origin's x-value.</td> * <td>Integer, between 0 and width of the image.</td> * </tr> * <tr><td><code>yorigin</code></td> * <td>The origin's y-value.</td> * <td>Integer, between 0 and height of the image.</td> * </tr> * <tr><td><code>width</code></td> * <td>The width of the cropped image.</td> * <td>Integer, between 1 and the width of the image minus xorigin.</td> * </tr> * <tr><td><code>height</code></td> * <td>The height of the cropped image.</td> * <td>Integer, between 1 and the height of the image minus yorigin.</td> * </tr> * </table> */ public final static String EFFECT_CROP = "android.media.effect.effects.CropEffect"; /** * <p>Applies a cross process effect on image, in which the red and green channels are * enhanced while the blue channel is restricted.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_CROSSPROCESS = "android.media.effect.effects.CrossProcessEffect"; /** * <p>Applies black and white documentary style effect on image..</p> * <p>Available parameters: None</p> */ public final static String EFFECT_DOCUMENTARY = "android.media.effect.effects.DocumentaryEffect"; /** * <p>Overlays a bitmap (with premultiplied alpha channel) onto the input image. The bitmap * is stretched to fit the input image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>bitmap</code></td> * <td>The overlay bitmap.</td> * <td>A non-null Bitmap instance.</td> * </tr> * </table> */ public final static String EFFECT_BITMAPOVERLAY = "android.media.effect.effects.BitmapOverlayEffect"; /** * <p>Representation of photo using only two color tones.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>first_color</code></td> * <td>The first color tone.</td> * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using * {@link android.graphics.Color Color} class.</td> * </tr> * <tr><td><code>second_color</code></td> * <td>The second color tone.</td> * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using * {@link android.graphics.Color Color} class.</td> * </tr> * </table> */ public final static String EFFECT_DUOTONE = "android.media.effect.effects.DuotoneEffect"; /** * <p>Applies back-light filling 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 strength of the backlight.</td> * <td>Float, between 0 and 1. Zero means no change.</td> * </tr> * </table> */ public final static String EFFECT_FILLLIGHT = "android.media.effect.effects.FillLightEffect"; /** * <p>Flips image vertically and/or horizontally.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>vertical</code></td> * <td>Whether to flip image vertically.</td> * <td>Boolean</td> * </tr> * <tr><td><code>horizontal</code></td> * <td>Whether to flip image horizontally.</td> * <td>Boolean</td> * </tr> * </table> */ public final static String EFFECT_FLIP = "android.media.effect.effects.FlipEffect"; /** * <p>Applies film grain effect to 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 strength of the grain effect.</td> * <td>Float, between 0 and 1. Zero means no change.</td> * </tr> * </table> */ public final static String EFFECT_GRAIN = "android.media.effect.effects.GrainEffect"; /** * <p>Converts image to grayscale.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_GRAYSCALE = "android.media.effect.effects.GrayscaleEffect"; /** * <p>Applies lomo-camera style effect to image.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_LOMOISH = "android.media.effect.effects.LomoishEffect"; /** * <p>Inverts the image colors.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_NEGATIVE = "android.media.effect.effects.NegativeEffect"; /** * <p>Applies posterization effect to image.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_POSTERIZE = "android.media.effect.effects.PosterizeEffect"; /** * <p>Removes red eyes on specified region.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>centers</code></td> * <td>Multiple center points (x, y) of the red eye regions.</td> * <td>An array of floats, where (f[2*i], f[2*i+1]) specifies the center of the i'th eye. * Coordinate values are expected to be normalized between 0 and 1.</td> * </tr> * </table> */ public final static String EFFECT_REDEYE = "android.media.effect.effects.RedEyeEffect"; /** * <p>Rotates the image. The output frame size must be able to fit the rotated version of * the input image. Note that the rotation snaps to a the closest multiple of 90 degrees.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>angle</code></td> * <td>The angle of rotation in degrees.</td> * <td>Integer value. This will be rounded to the nearest multiple of 90.</td> * </tr> * </table> */ public final static String EFFECT_ROTATE = "android.media.effect.effects.RotateEffect"; /** * <p>Adjusts color saturation of image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>scale</code></td> * <td>The scale of color saturation.</td> * <td>Float, between -1 and 1. 0 means no change, while -1 indicates full desaturation, * i.e. grayscale.</td> * </tr> * </table> */ public final static String EFFECT_SATURATE = "android.media.effect.effects.SaturateEffect"; /** * <p>Converts image to sepia tone.</p> * <p>Available parameters: None</p> */ public final static String EFFECT_SEPIA = "android.media.effect.effects.SepiaEffect"; /** * <p>Sharpens the image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>scale</code></td> * <td>The degree of sharpening.</td> * <td>Float, between 0 and 1. 0 means no change.</td> * </tr> * </table> */ public final static String EFFECT_SHARPEN = "android.media.effect.effects.SharpenEffect"; /** * <p>Rotates the image according to the specified angle, and crops the image so that no * non-image portions are visible.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>angle</code></td> * <td>The angle of rotation.</td> * <td>Float, between -45 and +45.</td> * </tr> * </table> */ public final static String EFFECT_STRAIGHTEN = "android.media.effect.effects.StraightenEffect"; /** * <p>Adjusts color temperature of the image.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>scale</code></td> * <td>The value of color temperature.</td> * <td>Float, between 0 and 1, with 0 indicating cool, and 1 indicating warm. A value of * of 0.5 indicates no change.</td> * </tr> * </table> */ public final static String EFFECT_TEMPERATURE = "android.media.effect.effects.ColorTemperatureEffect"; /** * <p>Tints the photo with specified color.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>tint</code></td> * <td>The color of the tint.</td> * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using * {@link android.graphics.Color Color} class.</td> * </tr> * </table> */ public final static String EFFECT_TINT = "android.media.effect.effects.TintEffect"; /** * <p>Adds a vignette effect to image, i.e. fades away the outer image edges.</p> * <p>Available parameters:</p> * <table> * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> * <tr><td><code>scale</code></td> * <td>The scale of vignetting.</td> * <td>Float, between 0 and 1. 0 means no change.</td> * </tr> * </table> */ public final static String EFFECT_VIGNETTE = "android.media.effect.effects.VignetteEffect"; EffectFactory(EffectContext effectContext) { mEffectContext = effectContext; } /** * Instantiate a new effect with the given effect name. * * <p>The effect's parameters will be set to their default values.</p> * * <p>Note that the EGL context associated with the current EffectContext need not be made * current when creating an effect. This allows the host application to instantiate effects * before any EGL context has become current.</p> * * @param effectName The name of the effect to create. * @return A new Effect instance. * @throws IllegalArgumentException if the effect with the specified name is not supported or * not known. */ public Effect createEffect(String effectName) { Class effectClass = getEffectClassByName(effectName); if (effectClass == null) { throw new IllegalArgumentException("Cannot instantiate unknown effect '" + effectName + "'!"); } return instantiateEffect(effectClass, effectName); } /** * Check if an effect is supported on this platform. * * <p>Some effects may only be available on certain platforms. Use this method before * instantiating an effect to make sure it is supported.</p> * * @param effectName The name of the effect. * @return true, if the effect is supported on this platform. * @throws IllegalArgumentException if the effect name is not known. */ public static boolean isEffectSupported(String effectName) { return getEffectClassByName(effectName) != null; } private static Class getEffectClassByName(String className) { Class effectClass = null; // Get context's classloader; otherwise cannot load non-framework effects ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // Look for the class in the imported packages for (String packageName : EFFECT_PACKAGES) { try { effectClass = contextClassLoader.loadClass(packageName + className); } catch (ClassNotFoundException e) { continue; } // Exit loop if class was found. if (effectClass != null) { break; } } return effectClass; } private Effect instantiateEffect(Class effectClass, String name) { // Make sure this is an Effect subclass try { effectClass.asSubclass(Effect.class); } catch (ClassCastException e) { throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass + "' which is not a subclass of Effect!", e); } // Look for the correct constructor Constructor effectConstructor = null; try { effectConstructor = effectClass.getConstructor(EffectContext.class, String.class); } catch (NoSuchMethodException e) { throw new RuntimeException("The effect class '" + effectClass + "' does not have " + "the required constructor.", e); } // Construct the effect Effect effect = null; try { effect = (Effect)effectConstructor.newInstance(mEffectContext, name); } catch (Throwable t) { throw new RuntimeException("There was an error constructing the effect '" + effectClass + "'!", t); } return effect; } }