content: /*
   * 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;
      }
  }
  